From 54f4aeb57b08012114e3d22fc937c0988e2bf4a2 Mon Sep 17 00:00:00 2001 From: "mjw@wray-m-3.hpl.hp.com" Date: Mon, 28 Jun 2004 15:03:15 +0000 Subject: [PATCH] bitkeeper revision 1.1010.1.11 (40e03333GhMB8qgYk92TiquiQmUajA) Change domain save/restore deal with the config string. --- .rootkeys | 40 + BitKeeper/etc/ignore | 18 + tools/lib/allocate.c | 116 +++ tools/lib/allocate.h | 45 ++ tools/lib/debug.h | 72 ++ tools/lib/enum.c | 61 ++ tools/lib/enum.h | 30 + tools/lib/file_stream.c | 202 +++++ tools/lib/file_stream.h | 35 + tools/lib/gzip_stream.c | 185 +++++ tools/lib/gzip_stream.h | 33 + tools/lib/hash_table.c | 640 +++++++++++++++ tools/lib/hash_table.h | 295 +++++++ tools/lib/iostream.c | 37 + tools/lib/iostream.h | 243 ++++++ tools/lib/kernel_stream.c | 177 ++++ tools/lib/kernel_stream.h | 29 + tools/lib/lexis.c | 93 +++ tools/lib/lexis.h | 122 +++ tools/lib/lzi_stream.c | 590 ++++++++++++++ tools/lib/lzi_stream.h | 36 + tools/lib/lzo_stream.c | 596 ++++++++++++++ tools/lib/lzo_stream.h | 36 + tools/lib/marshal.c | 207 +++++ tools/lib/marshal.h | 43 + tools/lib/socket_stream.c | 259 ++++++ tools/lib/socket_stream.h | 54 ++ tools/lib/string_stream.c | 174 ++++ tools/lib/string_stream.h | 46 ++ tools/lib/sxpr.c | 935 ++++++++++++++++++++++ tools/lib/sxpr.h | 413 ++++++++++ tools/lib/sxpr_parser.c | 897 +++++++++++++++++++++ tools/lib/sxpr_parser.h | 125 +++ tools/lib/sys_ctype.h | 12 + tools/lib/sys_net.c | 309 +++++++ tools/lib/sys_net.h | 78 ++ tools/lib/sys_string.c | 138 ++++ tools/lib/sys_string.h | 91 +++ tools/lib/xdr.c | 246 ++++++ tools/lib/xdr.h | 14 + tools/xc/lib/Makefile | 79 +- tools/xc/lib/xc.h | 14 +- tools/xc/lib/xc_io.c | 27 + tools/xc/lib/xc_io.h | 44 + tools/xc/lib/xc_linux_restore.c | 346 ++++---- tools/xc/lib/xc_linux_save.c | 320 ++++---- tools/xc/lib/xc_private.h | 3 + tools/xc/py/Xc.c | 361 ++------- tools/xc/py/setup.py | 11 +- tools/xen/lib/xend/XendDomain.py | 14 +- tools/xen/lib/xend/XendDomainInfo.py | 15 +- tools/xen/lib/xend/server/SrvDomain.py | 7 - tools/xen/lib/xend/server/SrvDomainDir.py | 13 + tools/xen/lib/xend/sxp.py | 23 + tools/xen/xend | 4 +- 55 files changed, 8365 insertions(+), 688 deletions(-) create mode 100644 tools/lib/allocate.c create mode 100644 tools/lib/allocate.h create mode 100644 tools/lib/debug.h create mode 100644 tools/lib/enum.c create mode 100644 tools/lib/enum.h create mode 100644 tools/lib/file_stream.c create mode 100644 tools/lib/file_stream.h create mode 100644 tools/lib/gzip_stream.c create mode 100644 tools/lib/gzip_stream.h create mode 100644 tools/lib/hash_table.c create mode 100644 tools/lib/hash_table.h create mode 100644 tools/lib/iostream.c create mode 100644 tools/lib/iostream.h create mode 100644 tools/lib/kernel_stream.c create mode 100644 tools/lib/kernel_stream.h create mode 100644 tools/lib/lexis.c create mode 100644 tools/lib/lexis.h create mode 100644 tools/lib/lzi_stream.c create mode 100644 tools/lib/lzi_stream.h create mode 100644 tools/lib/lzo_stream.c create mode 100644 tools/lib/lzo_stream.h create mode 100644 tools/lib/marshal.c create mode 100644 tools/lib/marshal.h create mode 100644 tools/lib/socket_stream.c create mode 100644 tools/lib/socket_stream.h create mode 100644 tools/lib/string_stream.c create mode 100644 tools/lib/string_stream.h create mode 100644 tools/lib/sxpr.c create mode 100644 tools/lib/sxpr.h create mode 100644 tools/lib/sxpr_parser.c create mode 100644 tools/lib/sxpr_parser.h create mode 100644 tools/lib/sys_ctype.h create mode 100644 tools/lib/sys_net.c create mode 100644 tools/lib/sys_net.h create mode 100644 tools/lib/sys_string.c create mode 100644 tools/lib/sys_string.h create mode 100644 tools/lib/xdr.c create mode 100644 tools/lib/xdr.h create mode 100644 tools/xc/lib/xc_io.c create mode 100644 tools/xc/lib/xc_io.h diff --git a/.rootkeys b/.rootkeys index 8418e7c668..adc1e74bcd 100644 --- a/.rootkeys +++ b/.rootkeys @@ -165,6 +165,44 @@ 40c9c468pXANclL7slGaoD0kSrIwoQ tools/examples/xm_dom_create.py 40cf2937oKlROYOJTN8GWwWM5AmjBg tools/examples/xmdefaults 40dfd40auJwNnb8NoiSnRkvZaaXkUg tools/examples/xmnetbsd +40e033325Sjqs-_4TuzeUEprP_gYFg tools/lib/allocate.c +40e03332KYz7o1bn2MG_KPbBlyoIMA tools/lib/allocate.h +40e03332IyRttYoXKoJla5qCC514SQ tools/lib/debug.h +40e03332qV5tJ-GJZjo-LBCeGuEjJA tools/lib/enum.c +40e03332wwMVxfobgA1PSMTSAGLiCw tools/lib/enum.h +40e03332p5Dc_owJQRuN72ymJZddFQ tools/lib/file_stream.c +40e03332jWfB2viAhLSkq1WK0r_iDQ tools/lib/file_stream.h +40e03332rUjNMGg11n2rN6V4DCrvOg tools/lib/gzip_stream.c +40e033321O5Qg22haLoq5lpmk4tooQ tools/lib/gzip_stream.h +40e03332QrTR96tc6yS2rMBpd2mq1A tools/lib/hash_table.c +40e033325KoIb0d_uy8s7b5DUR9fPQ tools/lib/hash_table.h +40e03332ihnBGzHykVwZnFmkAppb4g tools/lib/iostream.c +40e03332UGwbLR4wsw4ft14p0Yw5pg tools/lib/iostream.h +40e0333245DLDzJemeSVBLuutHtzEQ tools/lib/kernel_stream.c +40e03332aK0GkgpDdc-PVTkWKTeOBg tools/lib/kernel_stream.h +40e03332HJ0cDcZDKDUUT-tEiBWOZw tools/lib/lexis.c +40e03332tnH9Ggzxbfi3xY9Vh2hUlg tools/lib/lexis.h +40e03332aYIW0BNBh6wXuKKn_P7Yyg tools/lib/lzi_stream.c +40e0333233voTffE4cJSMGJARfiSSQ tools/lib/lzi_stream.h +40e03332FXuMoUnfsAKSgV8X4rFbYQ tools/lib/lzo_stream.c +40e03332InJaiLfpDcIXBy2fI0RFGQ tools/lib/lzo_stream.h +40e03332a5SCuRsejHStTuWzMQNv8Q tools/lib/marshal.c +40e03332TwKyJrZQiiQfNq4vc2hpgw tools/lib/marshal.h +40e033328ccHlJuTR1FswYL_EC6LFA tools/lib/socket_stream.c +40e03332P0KVQGkmahj47aafo1X0nA tools/lib/socket_stream.h +40e03332KT_tnnoAMbPVAZBB7kSOAQ tools/lib/string_stream.c +40e03332-VtK6_OZa1vMHXFil8uq6w tools/lib/string_stream.h +40e03332dDtczi6YX7_mMxhYjJeAdQ tools/lib/sxpr.c +40e03332QPuyNKDOTIYVvkwK5qO-vg tools/lib/sxpr.h +40e03332Pi0_osJ3XPBi38ADPqdl4A tools/lib/sxpr_parser.c +40e033324v5QFMvWEXXzv38uUT9kHg tools/lib/sxpr_parser.h +40e03332gKUInsqtxQOV4mPiMqf_dg tools/lib/sys_ctype.h +40e03332Rkvq6nn_UNjzAAK_Tk9v1g tools/lib/sys_net.c +40e03332lQHvQHw4Rh7VsT1_sui29A tools/lib/sys_net.h +40e033321smklZd7bDSdWvQCeIshtg tools/lib/sys_string.c +40e03332h5V611rRWURRLqb1Ekatxg tools/lib/sys_string.h +40e03332u4q5kgF0N7RfqB4s0pZVew tools/lib/xdr.c +40e03332hY16nfRXF4gGd5S1aUJUBw tools/lib/xdr.h 3f776bd2Xd-dUcPKlPN2vG89VGtfvQ tools/misc/Makefile 40ab2cfawIw8tsYo0dQKtp83h4qfTQ tools/misc/fakei386xen 3f6dc136ZKOjd8PIqLbFBl_v-rnkGg tools/misc/miniterm/Makefile @@ -185,6 +223,8 @@ 3fbba6dbasJQV-MVElDC0DGSHMiL5w tools/xc/lib/xc_domain.c 40278d99BLsfUv3qxv0I8C1sClZ0ow tools/xc/lib/xc_elf.h 403e0977Bjsm_e82pwvl9VvaJxh8Gg tools/xc/lib/xc_evtchn.c +40e03333Eegw8czSWvHsbKxrRZJjRA tools/xc/lib/xc_io.c +40e03333vrWGbLAhyJjXlqCHaJt7eA tools/xc/lib/xc_io.h 3fbba6dbNCU7U6nsMYiXzKkp3ztaJg tools/xc/lib/xc_linux_build.c 3fbba6dbl267zZOAVHYLOdLCdhcZMw tools/xc/lib/xc_linux_restore.c 3fbba6db7li3FJiABYtCmuGxOJxEGw tools/xc/lib/xc_linux_save.c diff --git a/BitKeeper/etc/ignore b/BitKeeper/etc/ignore index 51b5f03492..5db69fd7ef 100644 --- a/BitKeeper/etc/ignore +++ b/BitKeeper/etc/ignore @@ -31,3 +31,21 @@ xen/tools/figlet/figlet xen/xen xen/xen-syms xen/xen.* +tools/xc/lib/.allocate.o.d +tools/xc/lib/.file_stream.o.d +tools/xc/lib/.gzip_stream.o.d +tools/xc/lib/.iostream.o.d +tools/xc/lib/.sys_net.o.d +tools/xc/lib/.sys_string.o.d +tools/xc/lib/.xc_atropos.o.d +tools/xc/lib/.xc_bvtsched.o.d +tools/xc/lib/.xc_domain.o.d +tools/xc/lib/.xc_evtchn.o.d +tools/xc/lib/.xc_io.o.d +tools/xc/lib/.xc_linux_build.o.d +tools/xc/lib/.xc_linux_restore.o.d +tools/xc/lib/.xc_linux_save.o.d +tools/xc/lib/.xc_misc.o.d +tools/xc/lib/.xc_netbsd_build.o.d +tools/xc/lib/.xc_physdev.o.d +tools/xc/lib/.xc_private.o.d diff --git a/tools/lib/allocate.c b/tools/lib/allocate.c new file mode 100644 index 0000000000..600ebabda6 --- /dev/null +++ b/tools/lib/allocate.c @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2001 - 2004 Mike Wray + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "allocate.h" + +/** @file + * Support for allocating memory. + * Usable from user code or kernel code (with __KERNEL__ defined). + * In user code will use GC if USE_GC is defined. + */ + +#ifdef __KERNEL__ +/*----------------------------------------------------------------------------*/ +# include +# include +# include +# include + +# define DEFAULT_TYPE 0 +# define MALLOC(n, type) kmalloc(n, type) +# define FREE(ptr) kfree(ptr) + +/*----------------------------------------------------------------------------*/ +#else /* ! __KERNEL__ */ + +# include +# include + +# define DEFAULT_TYPE 0 + +#ifdef USE_GC +# include "gc.h" +# define MALLOC(n, typ) GC_malloc(n) +# define FREE(ptr) (ptr=NULL) +//typedef void *GC_PTR; +//GC_PTR (*GC_oom_fn)(size_t n); +#else +# define MALLOC(n, type) malloc(n) +# define FREE(ptr) free(ptr) +#endif + +/*----------------------------------------------------------------------------*/ +#endif + +/** Function to call when memory cannot be allocated. */ +AllocateFailedFn *allocate_failed_fn = NULL; + +/** Allocate memory and zero it. + * The type is only relevant when calling from kernel code, + * from user code it is ignored. + * In kernel code the values accepted by kmalloc can be used: + * GFP_USER, GFP_ATOMIC, GFP_KERNEL. + * + * @param size number of bytes to allocate + * @param type memory type to allocate (kernel only) + * @return pointer to the allocated memory or zero + * if malloc failed + */ +void *allocate_type(int size, int type){ + void *p = MALLOC(size, type); + if(p){ + memzero(p, size); + } else if(allocate_failed_fn){ + allocate_failed_fn(size, type); + } + return p; +} + +/** Allocate memory and zero it. + * + * @param size number of bytes to allocate + * @return pointer to the allocated memory or zero + * if malloc failed + */ +void *allocate(int size){ + return allocate_type(size, DEFAULT_TYPE); +} + +/** Free memory allocated by allocate(). + * No-op if 'p' is null. + * + * @param p memory to free + */ +void deallocate(void *p){ + if(p){ + FREE(p); + } +} + +/** Set bytes to zero. + * No-op if 'p' is null. + * + * @param p memory to zero + * @param size number of bytes to zero + */ +void memzero(void *p, int size){ + if(p){ + memset(p, 0, (size_t)size); + } +} + diff --git a/tools/lib/allocate.h b/tools/lib/allocate.h new file mode 100644 index 0000000000..08bc67b910 --- /dev/null +++ b/tools/lib/allocate.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2001 - 2004 Mike Wray + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _XEN_LIB_ALLOCATE_H_ +#define _XEN_LIB_ALLOCATE_H_ + +/** Allocate memory for a given type, and cast. */ +#define ALLOCATE(ctype) (ctype *)allocate(sizeof(ctype)) + +/** Allocate memory for a given type, and cast. */ +#define ALLOCATE_TYPE(ctype, type) (ctype *)allocate(sizeof(ctype)) + +extern void *allocate_type(int size, int type); +extern void *allocate(int size); +extern void deallocate(void *); +extern void memzero(void *p, int size); + +typedef void AllocateFailedFn(int size, int type); +extern AllocateFailedFn *allocate_failed_fn; + +#endif /* _XEN_LIB_ALLOCATE_H_ */ + + + + + + + + + diff --git a/tools/lib/debug.h b/tools/lib/debug.h new file mode 100644 index 0000000000..4f5228faa3 --- /dev/null +++ b/tools/lib/debug.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2004 Mike Wray + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _XEN_LIB_DEBUG_H_ +#define _XEN_LIB_DEBUG_H_ + +#ifndef MODULE_NAME +#define MODULE_NAME "" +#endif + +#ifdef __KERNEL__ +#include +#include + +#ifdef DEBUG + +#define dprintf(fmt, args...) printk(KERN_DEBUG "[DBG] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args) +#define wprintf(fmt, args...) printk(KERN_WARNING "[WRN] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args) +#define iprintf(fmt, args...) printk(KERN_INFO "[INF] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args) +#define eprintf(fmt, args...) printk(KERN_ERR "[ERR] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args) + +#else + +#define dprintf(fmt, args...) do {} while(0) +#define wprintf(fmt, args...) printk(KERN_WARNING "[WRN] " MODULE_NAME fmt, ##args) +#define iprintf(fmt, args...) printk(KERN_INFO "[INF] " MODULE_NAME fmt, ##args) +#define eprintf(fmt, args...) printk(KERN_ERR "[ERR] " MODULE_NAME fmt, ##args) + +#endif + +#else + +#include + +#ifdef DEBUG + +#define dprintf(fmt, args...) fprintf(stdout, "[DBG] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args) +#define wprintf(fmt, args...) fprintf(stderr, "[WRN] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args) +#define iprintf(fmt, args...) fprintf(stderr, "[INF] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args) +#define eprintf(fmt, args...) fprintf(stderr, "[ERR] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args) + +#else + +#define dprintf(fmt, args...) do {} while(0) +#define wprintf(fmt, args...) fprintf(stderr, "[WRN] " MODULE_NAME fmt, ##args) +#define iprintf(fmt, args...) fprintf(stderr, "[INF] " MODULE_NAME fmt, ##args) +#define eprintf(fmt, args...) fprintf(stderr, "[ERR] " MODULE_NAME fmt, ##args) + +#endif + +#endif + +/** Print format for an IP address. + * See NIPQUAD(), HIPQUAD() + */ +#define IPFMT "%u.%u.%u.%u" + +#endif /* ! _XEN_LIB_DEBUG_H_ */ diff --git a/tools/lib/enum.c b/tools/lib/enum.c new file mode 100644 index 0000000000..95f6e31a87 --- /dev/null +++ b/tools/lib/enum.c @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2002, 2004 Mike Wray + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. This library is + * distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef __KERNEL__ +#include +#else +#include +#endif + +#include "sys_string.h" +#include "enum.h" + +/** Map an enum name to its value using a table. + * + * @param name enum name + * @param defs enum definitions + * @return enum value or -1 if not known + */ +int enum_name_to_val(char *name, EnumDef *defs){ + int val = -1; + for(; defs->name; defs++){ + if(!strcmp(defs->name, name)){ + val = defs->val; + break; + } + } + return val; +} + +/** Map an enum value to its name using a table. + * + * @param val enum value + * @param defs enum definitions + * @param defs_n number of definitions + * @return enum name or NULL if not known + */ +char *enum_val_to_name(int val, EnumDef *defs){ + char *name = NULL; + for(; defs->name; defs++){ + if(val == defs->val){ + name = defs->name; + break; + } + } + return name; +} + diff --git a/tools/lib/enum.h b/tools/lib/enum.h new file mode 100644 index 0000000000..db6e7b0058 --- /dev/null +++ b/tools/lib/enum.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2002, 2004 Mike Wray + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. This library is + * distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _XEN_LIB_ENUM_H_ +#define _XEN_LIB_ENUM_H_ + +/** Mapping of an enum value to a name. */ +typedef struct EnumDef { + int val; + char *name; +} EnumDef; + +extern int enum_name_to_val(char *name, EnumDef *defs); +extern char *enum_val_to_name(int val, EnumDef *defs); + +#endif /* _XEN_LIB_ENUM_H_ */ diff --git a/tools/lib/file_stream.c b/tools/lib/file_stream.c new file mode 100644 index 0000000000..40391f7fa6 --- /dev/null +++ b/tools/lib/file_stream.c @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2001 - 2004 Mike Wray + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/** @file + * An IOStream implementation using FILE*. + */ +#ifndef __KERNEL__ +#include +#include +#include "allocate.h" +#include "file_stream.h" + +static int file_read(IOStream *s, void *buf, size_t n); +static int file_write(IOStream *s, const void *buf, size_t n); +static int file_error(IOStream *s); +static int file_close(IOStream *s); +static void file_free(IOStream *s); +static int file_flush(IOStream *s); + +/** Methods used by a FILE* IOStream. */ +static const IOMethods file_methods = { + read: file_read, + write: file_write, + error: file_error, + close: file_close, + free: file_free, + flush: file_flush, +}; + +/** IOStream for stdin. */ +static IOStream _iostdin = { + methods: &file_methods, + data: (void*)1, +}; + +/** IOStream for stdout. */ +static IOStream _iostdout = { + methods: &file_methods, + data: (void*)2, +}; + +/** IOStream for stderr. */ +static IOStream _iostderr = { + methods: &file_methods, + data: (void*)3, +}; + +/** IOStream for stdin. */ +IOStream *iostdin = &_iostdin; + +/** IOStream for stdout. */ +IOStream *iostdout = &_iostdout; + +/** IOStream for stderr. */ +IOStream *iostderr = &_iostderr; + +/** Get the underlying FILE*. + * + * @param s file stream + * @return the stream s wraps + */ +static inline FILE *get_file(IOStream *s){ + switch((long)s->data){ + case 1: s->data = stdin; break; + case 2: s->data = stdout; break; + case 3: s->data = stderr; break; + } + return (FILE*)s->data; +} + +/** Control buffering on the underlying stream, like setvbuf(). + * + * @param io file stream + * @param buf buffer + * @param mode buffering mode (see man setvbuf()) + * @param size buffer size + * @return 0 on success, non-zero otherwise + */ +int file_stream_setvbuf(IOStream *io, char *buf, int mode, size_t size){ + return setvbuf(get_file(io), buf, mode, size); +} + +/** Write to the underlying stream using fwrite(); + * + * @param stream input + * @param buf where to put input + * @param n number of bytes to write + * @return number of bytes written + */ +static int file_write(IOStream *s, const void *buf, size_t n){ + return fwrite(buf, 1, n, get_file(s)); +} + +/** Read from the underlying stream using fread(); + * + * @param stream input + * @param buf where to put input + * @param n number of bytes to read + * @return number of bytes read + */ +static int file_read(IOStream *s, void *buf, size_t n){ + return fread(buf, 1, n, get_file(s)); +} + +/** Fush the underlying stream using fflush(). + * + * @param s file stream + * @return 0 on success, error code otherwise + */ +static int file_flush(IOStream *s){ + return fflush(get_file(s)); +} + +/** Check if a stream has an error. + * + * @param s file stream + * @return 1 if has an error, 0 otherwise + */ +static int file_error(IOStream *s){ + return ferror(get_file(s)); +} + +/** Close a file stream. + * + * @param s file stream to close + * @return result of the close + */ +static int file_close(IOStream *s){ + return fclose(get_file(s)); +} + +/** Free a file stream. + * + * @param s file stream + */ +static void file_free(IOStream *s){ + // Do nothing - fclose does it all? +} + +/** Create an IOStream for a stream. + * + * @param f stream to wrap + * @return new IOStream using f for i/o + */ +IOStream *file_stream_new(FILE *f){ + IOStream *io = ALLOCATE(IOStream); + if(io){ + io->methods = &file_methods; + io->data = (void*)f; + } + return io; +} + +/** IOStream version of fopen(). + * + * @param file name of the file to open + * @param flags giving the mode to open in (as for fopen()) + * @return new stream for the open file, or 0 if failed + */ +IOStream *file_stream_fopen(const char *file, const char *flags){ + IOStream *io = 0; + FILE *fin = fopen(file, flags); + if(fin){ + io = file_stream_new(fin); + if(!io){ + fclose(fin); + //free(fin); // fclose frees ? + } + } + return io; +} + +/** IOStream version of fdopen(). + * + * @param fd file descriptor + * @param flags giving the mode to open in (as for fdopen()) + * @return new stream for the open file, or 0 if failed + */ +IOStream *file_stream_fdopen(int fd, const char *flags){ + IOStream *io = 0; + FILE *fin = fdopen(fd, flags); + if(fin){ + io = file_stream_new(fin); + } + return io; +} +#endif diff --git a/tools/lib/file_stream.h b/tools/lib/file_stream.h new file mode 100644 index 0000000000..36a0f928b2 --- /dev/null +++ b/tools/lib/file_stream.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2001 - 2004 Mike Wray + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _XEN_LIB_FILE_STREAM_H_ +#define _XEN_LIB_FILE_STREAM_H_ + +#ifndef __KERNEL__ +#include "iostream.h" +#include + +extern IOStream *file_stream_new(FILE *f); +extern IOStream *file_stream_fopen(const char *file, const char *flags); +extern IOStream *file_stream_fdopen(int fd, const char *flags); +extern IOStream get_stream_stdout(void); +extern IOStream get_stream_stderr(void); +extern IOStream get_stream_stdin(void); + +extern int file_stream_setvbuf(IOStream *io, char *buf, int mode, size_t size); +#endif +#endif /* !_XEN_LIB_FILE_STREAM_H_ */ diff --git a/tools/lib/gzip_stream.c b/tools/lib/gzip_stream.c new file mode 100644 index 0000000000..1e67b4d7d7 --- /dev/null +++ b/tools/lib/gzip_stream.c @@ -0,0 +1,185 @@ +/* $Id: gzip_stream.c,v 1.4 2003/09/30 15:22:53 mjw Exp $ */ +/* + * Copyright (C) 2003 Hewlett-Packard Company. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/** @file + * An IOStream implementation using zlib gzFile to provide + * compression and decompression. + */ +#ifndef __KERNEL__ + +#include +#include + +#include "zlib.h" + +extern FILE* gzfile(gzFile file); + +#include "allocate.h" +#include "gzip_stream.h" + +static int gzip_read(IOStream *s, void *buf, size_t n); +static int gzip_write(IOStream *s, const void *buf, size_t n); +static int gzip_error(IOStream *s); +static int gzip_close(IOStream *s); +static void gzip_free(IOStream *s); +static int gzip_flush(IOStream *s); + +/** Methods used by a gzFile* IOStream. */ +static const IOMethods gzip_methods = { + read: gzip_read, + write: gzip_write, + error: gzip_error, + close: gzip_close, + free: gzip_free, + flush: gzip_flush, +}; + +/** Get the underlying gzFile*. + * + * @param s gzip stream + * @return the stream s wraps + */ +static inline gzFile get_gzfile(IOStream *s){ + return (gzFile)s->data; +} + +/** Control buffering on the underlying stream, like setvbuf(). + * + * @param io gzip stream + * @param buf buffer + * @param mode buffering mode (see man setvbuf()) + * @param size buffer size + * @return 0 on success, non-zero otherwise + */ +int gzip_stream_setvbuf(IOStream *io, char *buf, int mode, size_t size){ + return setvbuf(gzfile(get_gzfile(io)), buf, mode, size); +} + +/** Write to the underlying stream. + * + * @param stream destination + * @param buf data + * @param n number of bytes to write + * @return number of bytes written + */ +static int gzip_write(IOStream *s, const void *buf, size_t n){ + return gzwrite(get_gzfile(s), (void*)buf, n); +} + +/** Read from the underlying stream. + * + * @param stream input + * @param buf where to put input + * @param n number of bytes to read + * @return number of bytes read + */ +static int gzip_read(IOStream *s, void *buf, size_t n){ + return gzread(get_gzfile(s), buf, n); +} + +/** Flush the underlying stream. + * + * @param s gzip stream + * @return 0 on success, error code otherwise + */ +static int gzip_flush(IOStream *s){ + //return gzflush(get_gzfile(s), Z_NO_FLUSH); + return gzflush(get_gzfile(s), Z_SYNC_FLUSH); + //return gzflush(get_gzfile(s), Z_FULL_FLUSH); +} + +/** Check if a stream has an error. + * + * @param s gzip stream + * @return 1 if has an error, 0 otherwise + */ +static int gzip_error(IOStream *s){ + int err; + gzFile *gz = get_gzfile(s); + gzerror(gz, &err); + return (err == Z_ERRNO ? ferror(gzfile(gz)) : err); +} + +/** Close a gzip stream. + * + * @param s gzip stream to close + * @return result of the close + */ +static int gzip_close(IOStream *s){ + return gzclose(get_gzfile(s)); +} + +/** Free a gzip stream. + * + * @param s gzip stream + */ +static void gzip_free(IOStream *s){ + // Do nothing - fclose does it all? +} + +/** Create an IOStream for a gzip stream. + * + * @param f stream to wrap + * @return new IOStream using f for i/o + */ +IOStream *gzip_stream_new(gzFile *f){ + IOStream *io = ALLOCATE(IOStream); + if(io){ + io->methods = &gzip_methods; + io->data = (void*)f; + } + return io; +} + +/** IOStream version of fopen(). + * + * @param file name of the file to open + * @param flags giving the mode to open in (as for fopen()) + * @return new stream for the open file, or NULL if failed + */ +IOStream *gzip_stream_fopen(const char *file, const char *flags){ + IOStream *io = NULL; + gzFile *fgz; + fgz = gzopen(file, flags); + if(fgz){ + io = gzip_stream_new(fgz); + if(!io){ + gzclose(fgz); + //free(fgz); // gzclose frees ? + } + } + return io; +} + +/** IOStream version of fdopen(). + * + * @param fd file descriptor + * @param flags giving the mode to open in (as for fdopen()) + * @return new stream for the open file, or NULL if failed + */ +IOStream *gzip_stream_fdopen(int fd, const char *flags){ + IOStream *io = NULL; + gzFile *fgz; + fgz = gzdopen(fd, flags); + if(fgz){ + io = gzip_stream_new(fgz); + } + return io; +} +#endif diff --git a/tools/lib/gzip_stream.h b/tools/lib/gzip_stream.h new file mode 100644 index 0000000000..63acb8cf1b --- /dev/null +++ b/tools/lib/gzip_stream.h @@ -0,0 +1,33 @@ +#/* $Id: gzip_stream.h,v 1.3 2003/09/30 15:22:53 mjw Exp $ */ +/* + * Copyright (C) 2003 Hewlett-Packard Company. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _SP_GZIP_STREAM_H_ +#define _SP_GZIP_STREAM_H_ + +#ifndef __KERNEL__ +#include "iostream.h" +#include "zlib.h" + +extern IOStream *gzip_stream_new(gzFile *f); +extern IOStream *gzip_stream_fopen(const char *file, const char *flags); +extern IOStream *gzip_stream_fdopen(int fd, const char *flags); + +extern int gzip_stream_setvbuf(IOStream *io, char *buf, int mode, size_t size); +#endif +#endif /* !_SP_FILE_STREAM_H_ */ diff --git a/tools/lib/hash_table.c b/tools/lib/hash_table.c new file mode 100644 index 0000000000..13da946e77 --- /dev/null +++ b/tools/lib/hash_table.c @@ -0,0 +1,640 @@ +/* + * Copyright (C) 2001 - 2004 Mike Wray + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef __KERNEL__ +# include +# include +# include +# include +#else +# include +# include +#endif + +//#include + +#include "allocate.h" +#include "hash_table.h" + +/** @file + * Base support for hashtables. + * + * Hash codes are reduced modulo the number of buckets to index tables, + * so there is no need for hash functions to limit the range of hashcodes. + * In fact it is assumed that hashcodes do not change when the number of + * buckets in the table changes. + */ + +/*==========================================================================*/ +/** Number of bits in half a word. */ +//#if __WORDSIZE == 64 +//#define HALF_WORD_BITS 32 +//#else +#define HALF_WORD_BITS 16 +//#endif + +/** Mask for lo half of a word. On 32-bit this is + * (1<<16) - 1 = 65535 = 0xffff + * It's 4294967295 = 0xffffffff on 64-bit. + */ +#define LO_HALF_MASK ((1 << HALF_WORD_BITS) - 1) + +/** Get the lo half of a word. */ +#define LO_HALF(x) ((x) & LO_HALF_MASK) + +/** Get the hi half of a word. */ +#define HI_HALF(x) ((x) >> HALF_WORD_BITS) + +/** Do a full hash on both inputs, using DES-style non-linear scrambling. + * Both inputs are replaced with the results of the hash. + * + * @param pleft input/output word + * @param pright input/output word + */ +void pseudo_des(unsigned long *pleft, unsigned long *pright){ + // Bit-rich mixing constant. + static const unsigned long a_mixer[] = { + 0xbaa96887L, 0x1e17d32cL, 0x03bcdc3cL, 0x0f33d1b2L, }; + + // Bit-rich mixing constant. + static const unsigned long b_mixer[] = { + 0x4b0f3b58L, 0xe874f0c3L, 0x6955c5a6L, 0x55a7ca46L, }; + + // Number of iterations - must be 2 or 4. + static const int ncycle = 4; + //static const int ncycle = 2; + + unsigned long left = *pleft, right = *pright; + unsigned long v, v_hi, v_lo; + int i; + + for(i=0; ibuckets + (hashcode % table->buckets_n); +} + +/** Initialize a hash table. + * Can be safely called more than once. + * + * @param table to initialize + */ +void HashTable_init(HashTable *table){ + int i; + + if(!table->init_done){ + table->init_done = 1; + table->next_id = 0; + for(i=0; ibuckets_n; i++){ + HTBucket *bucket = get_bucket(table, i); + bucket->head = 0; + bucket->count = 0; + } + table->entry_count = 0; + } +} + +/** Allocate a new hashtable. + * If the number of buckets is not positive the default is used. + * The number of buckets should usually be prime. + * + * @param buckets_n number of buckets + * @return new hashtable or null + */ +HashTable *HashTable_new(int buckets_n){ + HashTable *z = ALLOCATE(HashTable); + if(!z) goto exit; + if(buckets_n <= 0){ + buckets_n = HT_BUCKETS_N; + } + z->buckets = (HTBucket*)allocate(buckets_n * sizeof(HTBucket)); + if(!z->buckets){ + deallocate(z); + z = 0; + goto exit; + } + z->buckets_n = buckets_n; + HashTable_init(z); + exit: + return z; +} + +/** Free a hashtable. + * Any entries are removed and freed. + * + * @param h hashtable (ignored if null) + */ +void HashTable_free(HashTable *h){ + if(h){ + HashTable_clear(h); + deallocate(h->buckets); + deallocate(h); + } +} + +/** Push an entry on the list in the bucket for a given hashcode. + * + * @param table to add entry to + * @param hashcode for the entry + * @param entry to add + */ +static inline void push_on_bucket(HashTable *table, Hashcode hashcode, + HTEntry *entry){ + HTBucket *bucket; + HTEntry *old_head; + + bucket = get_bucket(table, hashcode); + old_head = bucket->head; + bucket->count++; + bucket->head = entry; + entry->next = old_head; +} + +/** Change the number of buckets in a hashtable. + * No-op if the number of buckets is not positive. + * Existing entries are reallocated to buckets based on their hashcodes. + * The table is unmodified if the number of buckets cannot be changed. + * + * @param table hashtable + * @param buckets_n new number of buckets + * @return 0 on success, error code otherwise + */ +int HashTable_set_buckets_n(HashTable *table, int buckets_n){ + int err = 0; + HTBucket *old_buckets = table->buckets; + int old_buckets_n = table->buckets_n; + int i; + + if(buckets_n <= 0){ + err = -EINVAL; + goto exit; + } + table->buckets = (HTBucket*)allocate(buckets_n * sizeof(HTBucket)); + if(!table->buckets){ + err = -ENOMEM; + table->buckets = old_buckets; + goto exit; + } + table->buckets_n = buckets_n; + for(i=0; ihead; entry; entry = next){ + next = entry->next; + push_on_bucket(table, entry->hashcode, entry); + } + } + deallocate(old_buckets); + exit: + return err; +} + +/** Adjust the number of buckets so the table is neither too full nor too empty. + * The table is unmodified if adjusting fails. + * + * @param table hash table + * @param buckets_min minimum number of buckets (use default if 0 or negative) + * @return 0 on success, error code otherwise + */ +int HashTable_adjust(HashTable *table, int buckets_min){ + int buckets_n = 0; + int err = 0; + if(buckets_min <= 0) buckets_min = HT_BUCKETS_N; + if(table->entry_count >= table->buckets_n){ + // The table is dense - expand it. + buckets_n = 2 * table->buckets_n; + } else if((table->buckets_n > buckets_min) && + (4 * table->entry_count < table->buckets_n)){ + // The table is more than minimum size and sparse - shrink it. + buckets_n = 2 * table->entry_count; + if(buckets_n < buckets_min) buckets_n = buckets_min; + } + if(buckets_n){ + err = HashTable_set_buckets_n(table, buckets_n); + } + return err; +} + +/** Allocate a new entry for a given value. + * + * @param value to put in the entry + * @return entry, or 0 on failure + */ +HTEntry * HTEntry_new(Hashcode hashcode, void *key, void *value){ + HTEntry *z = ALLOCATE(HTEntry); + if(z){ + z->hashcode = hashcode; + z->key = key; + z->value = value; + } + return z; +} + +/** Free an entry. + * + * @param z entry to free + */ +inline void HTEntry_free(HTEntry *z){ + if(z){ + deallocate(z); + } +} + +/** Free an entry in a hashtable. + * The table's entry_free_fn is used is defined, otherwise + * the HTEntry itself is freed. + * + * @param table hashtable + * @param entry to free + */ +inline void HashTable_free_entry(HashTable *table, HTEntry *entry){ + if(!entry)return; + if(table && table->entry_free_fn){ + table->entry_free_fn(table, entry); + } else { + HTEntry_free(entry); + } +} + +/** Get the first entry satisfying a test from the bucket for the + * given hashcode. + * + * @param table to look in + * @param hashcode indicates the bucket + * @param test_fn test to apply to elements + * @param arg first argument to calls to test_fn + * @return entry found, or 0 + */ +inline HTEntry * HashTable_find_entry(HashTable *table, Hashcode hashcode, + TableTestFn *test_fn, TableArg arg){ + HTBucket *bucket; + HTEntry *entry = 0; + HTEntry *next; + + bucket = get_bucket(table, hashcode); + for(entry = bucket->head; entry; entry = next){ + next = entry->next; + if(test_fn(arg, table, entry)){ + break; + } + } + return entry; +} + +/** Test hashtable keys for equality. + * Uses the table's key_equal_fn if defined, otherwise pointer equality. + * + * @param key1 key to compare + * @param key2 key to compare + * @return 1 if equal, 0 otherwise + */ +inline int HashTable_key_equal(HashTable *table, void *key1, void *key2){ + return (table->key_equal_fn ? table->key_equal_fn(key1, key2) : key1==key2); +} + +/** Compute the hashcode of a hashtable key. + * The table's key_hash_fn is used if defined, otherwise the address of + * the key is hashed. + * + * @param table hashtable + * @param key to hash + * @return hashcode + */ +inline Hashcode HashTable_key_hash(HashTable *table, void *key){ + return (table->key_hash_fn ? table->key_hash_fn(key) : hash_ul((unsigned long)key)); +} + +/** Test if an entry has a given key. + * + * @param arg containing key to test for + * @param table the entry is in + * @param entry to test + * @return 1 if the entry has the key, 0 otherwise + */ +static inline int has_key(TableArg arg, HashTable *table, HTEntry *entry){ + return HashTable_key_equal(table, arg.ptr, entry->key); +} + +/** Get an entry with a given key. + * + * @param table to search + * @param key to look for + * @return entry if found, null otherwise + */ +#if 0 +inline HTEntry * HashTable_get_entry(HashTable *table, void *key){ + TableArg arg = { ptr: key }; + return HashTable_find_entry(table, HashTable_key_hash(table, key), has_key, arg); +} +#else +inline HTEntry * HashTable_get_entry(HashTable *table, void *key){ + Hashcode hashcode; + HTBucket *bucket; + HTEntry *entry = 0; + HTEntry *next; + + hashcode = HashTable_key_hash(table, key); + bucket = get_bucket(table, hashcode); + for(entry = bucket->head; entry; entry = next){ + next = entry->next; + if(HashTable_key_equal(table, key, entry->key)){ + break; + } + } + return entry; +} +#endif + +/** Get the value of an entry with a given key. + * + * @param table to search + * @param key to look for + * @return value if an entry was found, null otherwise + */ +inline void * HashTable_get(HashTable *table, void *key){ + HTEntry *entry = HashTable_get_entry(table, key); + return (entry ? entry->value : 0); +} + +/** Print the buckets in a table. + * + * @param table to print + */ +void show_buckets(HashTable *table, IOStream *io){ + int i,j ; + IOStream_print(io, "entry_count=%d buckets_n=%d\n", table->entry_count, table->buckets_n); + for(i=0; ibuckets_n; i++){ + if(0 || table->buckets[i].count>0){ + IOStream_print(io, "bucket %3d %3d %10p ", i, + table->buckets[i].count, + table->buckets[i].head); + for(j = table->buckets[i].count; j>0; j--){ + IOStream_print(io, "+"); + } + IOStream_print(io, "\n"); + } + } + HashTable_print(table, io); +} + +/** Print an entry in a table. + * + * @param entry to print + * @param arg a pointer to an IOStream to print to + * @return 0 + */ +static int print_entry(TableArg arg, HashTable *table, HTEntry *entry){ + IOStream *io = (IOStream*)arg.ptr; + IOStream_print(io, " b=%4lx h=%08lx i=%08lx |-> e=%8p k=%8p v=%8p\n", + entry->hashcode % table->buckets_n, + entry->hashcode, + entry->index, + entry, entry->key, entry->value); + return 0; +} + +/** Print a hash table. + * + * @param table to print + */ +void HashTable_print(HashTable *table, IOStream *io){ + IOStream_print(io, "{\n"); + HashTable_map(table, print_entry, (TableArg){ ptr: io }); + IOStream_print(io, "}\n"); +} +/*==========================================================================*/ + +/** Get the next entry id to use for a table. + * + * @param table hash table + * @return non-zero entry id + */ +static inline unsigned long get_next_id(HashTable *table){ + unsigned long id; + + if(table->next_id == 0){ + table->next_id = 1; + } + id = table->next_id++; + return id; +} + +/** Add an entry to the bucket for the + * given hashcode. + * + * @param table to insert in + * @param hashcode indicates the bucket + * @param key to add an entry for + * @param value to add an entry for + * @return entry on success, 0 on failure + */ +inline HTEntry * HashTable_add_entry(HashTable *table, Hashcode hashcode, void *key, void *value){ + HTEntry *entry = HTEntry_new(hashcode, key, value); + if(entry){ + entry->index = get_next_id(table); + push_on_bucket(table, hashcode, entry); + table->entry_count++; + } + return entry; +} + +/** Move the front entry for a bucket to the correct point in the bucket order as + * defined by the order function. If this is called every time a new entry is added + * the bucket will be maintained in sorted order. + * + * @param table to modify + * @param hashcode indicates the bucket + * @param order entry comparison function + * @return 0 if an entry was moved, 1 if not + */ +int HashTable_order_bucket(HashTable *table, Hashcode hashcode, TableOrderFn *order){ + HTEntry *new_entry = NULL, *prev = NULL, *entry = NULL; + HTBucket *bucket; + int err = 1; + + bucket = get_bucket(table, hashcode); + new_entry = bucket->head; + if(!new_entry || !new_entry->next) goto exit; + for(entry = new_entry->next; entry; prev = entry, entry = entry->next){ + if(order(new_entry, entry) <= 0) break; + } + if(prev){ + err = 0; + bucket->head = new_entry->next; + new_entry->next = entry; + prev->next = new_entry; + } + exit: + return err; +} + +/** Add an entry to a hashtable. + * The entry is added to the bucket for its key's hashcode. + * + * @param table to insert in + * @param key to add an entry for + * @param value to add an entry for + * @return entry on success, 0 on failure + */ +inline HTEntry * HashTable_add(HashTable *table, void *key, void *value){ + return HashTable_add_entry(table, HashTable_key_hash(table, key), key, value); +} + + +/** Remove entries satisfying a test from the bucket for the + * given hashcode. + * + * @param table to remove from + * @param hashcode indicates the bucket + * @param test_fn test to apply to elements + * @param arg first argument to calls to test_fn + * @return number of entries removed + */ +inline int HashTable_remove_entry(HashTable *table, Hashcode hashcode, + TableTestFn *test_fn, TableArg arg){ + HTBucket *bucket; + HTEntry *entry, *prev = 0, *next; + int removed_count = 0; + + bucket = get_bucket(table, hashcode); + for(entry = bucket->head; entry; entry = next){ + next = entry->next; + if(test_fn(arg, table, entry)){ + if(prev){ + prev->next = next; + } else { + bucket->head = next; + } + bucket->count--; + table->entry_count--; + removed_count++; + HashTable_free_entry(table, entry); + entry = 0; + } + prev = entry; + } + return removed_count; +} + +/** Remove entries with a given key. + * + * @param table to remove from + * @param key of entries to remove + * @return number of entries removed + */ +inline int HashTable_remove(HashTable *table, void *key){ +#if 1 + Hashcode hashcode; + HTBucket *bucket; + HTEntry *entry, *prev = 0, *next; + int removed_count = 0; + + hashcode = HashTable_key_hash(table, key); + bucket = get_bucket(table, hashcode); + for(entry = bucket->head; entry; entry = next){ + next = entry->next; + if(HashTable_key_equal(table, key, entry->key)){ + if(prev){ + prev->next = next; + } else { + bucket->head = next; + } + bucket->count--; + table->entry_count--; + removed_count++; + HashTable_free_entry(table, entry); + entry = 0; + } + prev = entry; + } + return removed_count; +#else + return HashTable_remove_entry(table, HashTable_key_hash(table, key), + has_key, (TableArg){ ptr: key}); +#endif +} + +/** Remove (and free) all the entries in a bucket. + * + * @param bucket to clear + */ +static inline void bucket_clear(HashTable *table, HTBucket *bucket){ + HTEntry *entry, *next; + + for(entry = bucket->head; entry; entry = next){ + next = entry->next; + HashTable_free_entry(table, entry); + } + bucket->head = 0; + table->entry_count -= bucket->count; + bucket->count = 0; +} + +/** Remove (and free) all the entries in a table. + * + * @param table to clear + */ +void HashTable_clear(HashTable *table){ + int i, n = table->buckets_n; + + for(i=0; ibuckets + i); + } +} diff --git a/tools/lib/hash_table.h b/tools/lib/hash_table.h new file mode 100644 index 0000000000..6d7e76ff33 --- /dev/null +++ b/tools/lib/hash_table.h @@ -0,0 +1,295 @@ +/* $Id: hash_table.h,v 1.1 2004/03/30 16:21:26 mjw Exp $ */ +/* + * Copyright (C) 2001 - 2004 Mike Wray + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _XEN_LIB_HASH_TABLE_H_ +#define _XEN_LIB_HASH_TABLE_H_ + +#include "iostream.h" + +typedef unsigned long Hashcode; + +/** Type used to pass parameters to table functions. */ +typedef union TableArg { + unsigned long ul; + void *ptr; +} TableArg; + +/** An entry in a bucket list. */ +typedef struct HTEntry { + /** Hashcode of the entry's key. */ + Hashcode hashcode; + /** Identifier for this entry in the table. */ + int index; + /** The key for this entry. */ + void *key; + /** The value in this entry. */ + void *value; + /** The next entry in the list. */ + struct HTEntry *next; +} HTEntry; + +/** A bucket in a rule table. */ +typedef struct HTBucket { + /** Number of entries in the bucket. */ + int count; + /** First entry in the bucket (may be null). */ + HTEntry *head; +} HTBucket; + +/** Default number of buckets in a hash table. + * You want enough buckets so the lists in the buckets will typically be short. + * It's a good idea if this is prime, since that will help to spread hashcodes + * around the table. + */ +//#define HT_BUCKETS_N 1 +//#define HT_BUCKETS_N 3 +//#define HT_BUCKETS_N 7 +//#define HT_BUCKETS_N 17 +//#define HT_BUCKETS_N 97 +//#define HT_BUCKETS_N 211 +//#define HT_BUCKETS_N 401 +#define HT_BUCKETS_N 1021 + +typedef struct HashTable HashTable; + +/** Type for a function used to select table entries. */ +typedef int TableTestFn(TableArg arg, HashTable *table, HTEntry *entry); + +/** Type for a function to map over table entries. */ +typedef int TableMapFn(TableArg arg, HashTable *table, HTEntry *entry); + +/** Type for a function to free table entries. */ +typedef void TableFreeFn(HashTable *table, HTEntry *entry); + +/** Type for a function to hash table keys. */ +typedef Hashcode TableHashFn(void *key); + +/** Type for a function to test table keys for equality. */ +typedef int TableEqualFn(void *key1, void *key2); + +/** Type for a function to order table entries. */ +typedef int TableOrderFn(HTEntry *e1, HTEntry *e2); + +/** General hash table. + * A hash table with a list in each bucket. + * Functions can be supplied for freeing entries, hashing keys, and comparing keys. + * These all default to 0, when default behaviour treating keys as integers is used. + */ +struct HashTable { + /** Flag indicating whether the table has been initialised. */ + int init_done; + /** Next value for the id field in inserted rules. */ + unsigned long next_id; + /** Number of buckets in the bucket array. */ + int buckets_n; + /** Array of buckets, each with its own list. */ + HTBucket *buckets; + /** Number of entries in the table. */ + int entry_count; + /** Function to free keys and values in entries. */ + TableFreeFn *entry_free_fn; + /** Function to hash keys. */ + TableHashFn *key_hash_fn; + /** Function to compare keys for equality. */ + TableEqualFn *key_equal_fn; + /** Place for the user of the table to hang extra data. */ + void *user_data; +}; + +extern HashTable *HashTable_new(int bucket_n); +extern void HashTable_free(HashTable *table); +extern HTEntry * HTEntry_new(Hashcode hashcode, void *key, void *value); +extern void HTEntry_free(HTEntry *entry); +extern int HashTable_set_bucket_n(HashTable *table, int bucket_n); +extern void HashTable_clear(HashTable *table); +extern HTEntry * HashTable_add_entry(HashTable *table, Hashcode hashcode, void *key, void *value); +extern HTEntry * HashTable_get_entry(HashTable *table, void *key); +extern HTEntry * HashTable_add(HashTable *table, void *key, void *value); +extern void * HashTable_get(HashTable *table, void *key); +extern int HashTable_remove(HashTable *table, void *key); +extern HTEntry * HashTable_find_entry(HashTable *table, Hashcode hashcode, + TableTestFn *test_fn, TableArg arg); +extern int HashTable_remove_entry(HashTable *table, Hashcode hashcode, + TableTestFn *test_fn, TableArg arg); +//extern int HashTable_map(HashTable *table, TableMapFn *map_fn, TableArg arg); +extern void HashTable_print(HashTable *table, IOStream *out); +extern int HashTable_set_buckets_n(HashTable *table, int buckets_n); +extern int HashTable_adjust(HashTable *table, int buckets_min); +extern void pseudo_des(unsigned long *pleft, unsigned long *pright); +extern Hashcode hash_string(char *s); + +extern int HashTable_order_bucket(HashTable *table, Hashcode hashcode, TableOrderFn *order); + +/** Control whether to use hashing based on DES or simple + * hashing. DES hashing is `more random' but much more expensive. + */ +#define HASH_PSEUDO_DES 0 + +/** Hash a long using a quick and dirty linear congruential random number generator. + * See `Numerical Recipes in C', Chapter 7, "An Even Quicker Generator". + * + * @param a value to hash + * @return hashed input + */ +static inline unsigned long lcrng_hash(unsigned long a){ + return (1664525L * a + 1013904223L); +} + +/** Hash an unsigned long. + * + * @param a input to hash + * @return hashcode + */ +static inline Hashcode hash_ul(unsigned long a){ +#if HASH_PSEUDO_DES + unsigned long left = a; + unsigned long right = 0L; + pseudo_des(&left, &right); + return right; +#else + a = lcrng_hash(a); + a = lcrng_hash(a); + return a; +#endif +} + +/** Hash two unsigned longs together. + * + * @param a input to hash + * @param b input to hash + * @return hashcode + */ +static inline Hashcode hash_2ul(unsigned long a, unsigned long b){ +#if HASH_PSEUDO_DES + unsigned long left = a; + unsigned long right = b; + pseudo_des(&left, &right); + return right; +#else + a = lcrng_hash(a); + a ^= b; + a = lcrng_hash(a); + return a; +#endif +} + +/** Hash a hashcode and an unsigned long together. + * + * @param a input hashcode + * @param b input to hash + * @return hashcode + */ +static inline Hashcode hash_hul(Hashcode a, unsigned long b){ +#if HASH_PSEUDO_DES + unsigned long left = a; + unsigned long right = b; + pseudo_des(&left, &right); + return right; +#else + a ^= b; + a = lcrng_hash(a); + return a; +#endif +} + +/** Macro to declare variables for HashTable_for_each() to use. + * + * @param entry variable that is set to entries in the table + */ +#define HashTable_for_decl(entry) \ + HashTable *_var_table; \ + HTBucket *_var_bucket; \ + HTBucket *_var_end; \ + HTEntry *_var_next; \ + HTEntry *entry + +/** Macro to iterate over the entries in a hashtable. + * Must be in a scope where HashTable_for_decl() has been used to declare + * variables for it to use. + * The variable 'entry' is iterated over entries in the table. + * The code produced is syntactically a loop, so it must be followed by + * a loop body, typically some statements in braces: + * HashTable_for_each(entry, table){ ...loop body... } + * + * HashTable_for_each() and HashTable_for_decl() cannot be used for nested + * loops as variables will clash. + * + * @note The simplest way to code a direct loop over the entries in a hashtable + * is to use a loop over the buckets, with a nested loop over the entries + * in a bucket. Using this approach in a macro means the macro contains + * an opening brace, and calls to it must be followed by 2 braces! + * To avoid this the code has been restructured so that it is a for loop. + * So that statements could be used in the test expression of the for loop, + * we have used the gcc statement expression extension ({ ... }). + * + * @param entry variable to iterate over the entries + * @param table to iterate over (non-null) + */ +#define HashTable_for_each(entry, table) \ + _var_table = table; \ + _var_bucket = _var_table->buckets; \ + _var_end = _var_bucket + _var_table->buckets_n; \ + for(entry=0, _var_next=0; \ + ({ if(_var_next){ \ + entry = _var_next; \ + _var_next = entry->next; \ + } else { \ + while(_var_bucket < _var_end){ \ + entry = _var_bucket->head; \ + _var_bucket++; \ + if(entry){ \ + _var_next = entry->next; \ + break; \ + } \ + } \ + }; \ + entry; }); \ + entry = _var_next ) + +/** Map a function over the entries in a table. + * Mapping stops when the function returns a non-zero value. + * Uses the gcc statement expression extension ({ ... }). + * + * @param table to map over + * @param fn function to apply to entries + * @param arg first argument to call the function with + * @return 0 if fn always returned 0, first non-zero value otherwise + */ +#define HashTable_map(table, fn, arg) \ + ({ HashTable_for_decl(_var_entry); \ + TableArg _var_arg = arg; \ + int _var_value = 0; \ + HashTable_for_each(_var_entry, table){ \ + if((_var_value = fn(_var_arg, _var_table, _var_entry))) break; \ + } \ + _var_value; }) + +/** Cast x to the type for a key or value in a hash table. + * This avoids compiler warnings when using short integers + * as keys or values (especially on 64-bit platforms). + */ +#define HKEY(x) ((void*)(unsigned long)(x)) + +/** Cast x from the type for a key or value in a hash table. + * to an unsigned long. This avoids compiler warnings when using + * short integers as keys or values (especially on 64-bit platforms). + */ +#define HVAL(x) ((unsigned long)(x)) + +#endif /* !_XEN_LIB_HASH_TABLE_H_ */ diff --git a/tools/lib/iostream.c b/tools/lib/iostream.c new file mode 100644 index 0000000000..e9980838f7 --- /dev/null +++ b/tools/lib/iostream.c @@ -0,0 +1,37 @@ +#include "iostream.h" +#include "sys_string.h" + +/** Print on a stream, like vfprintf(). + * + * @param stream to print to + * @param format for the print (as fprintf()) + * @param args arguments to print + * @return result code from the print + */ +int IOStream_vprint(IOStream *stream, const char *format, va_list args){ + char buffer[1024]; + int k = sizeof(buffer), n; + + n = vsnprintf(buffer, k, (char*)format, args); + if(n < 0 || n > k ){ + n = k; + } + n = IOStream_write(stream, buffer, n); + return n; +} + +/** Print on a stream, like fprintf(). + * + * @param stream to print to + * @param format for the print (as fprintf()) + * @return result code from the print + */ +int IOStream_print(IOStream *stream, const char *format, ...){ + va_list args; + int result = -1; + + va_start(args, format); + result = IOStream_vprint(stream, format, args); + va_end(args); + return result; +} diff --git a/tools/lib/iostream.h b/tools/lib/iostream.h new file mode 100644 index 0000000000..5dbe14a0b4 --- /dev/null +++ b/tools/lib/iostream.h @@ -0,0 +1,243 @@ +#ifndef _XC_LINUX_SAVE_H_ +#define _XC_LINUX_SAVE_H_ + +#include +#include +#include + +#ifdef __KERNEL__ +#include +#else +#include +#endif + +#include "allocate.h" + +/** End of input return value. */ +#define IOSTREAM_EOF -1 + +/** An input/output abstraction. + */ +typedef struct IOStream IOStream; + +/** Record of the functions to use for operations on an + * IOStream implementation. + */ +typedef struct IOMethods { + /** Read function. Called with the user data, buffer to read into + * and number of bytes to read. Must return number of bytes read + * on success, less than zero on error. + */ + int (*read)(IOStream *stream, void *buf, size_t n); + + /** Write function. Called with user data, buffer to write and + * number of bytes to write. Must return number of bytes written on + * success, less than zero otherwise. + */ + int (*write)(IOStream *stream, const void *buf, size_t n); + + int (*flush)(IOStream *s); + + int (*error)(IOStream *s); + + int (*close)(IOStream *s); + + void (*free)(IOStream *s); + + void (*lock)(IOStream *s); + void (*unlock)(IOStream *s); + +} IOMethods; + +/** Abstract i/o object. + */ +struct IOStream { + /** Methods to use to implement operations. */ + const IOMethods *methods; + /** Private state for the implementation. */ + const void *data; + /** Flag indicating whether the stream is closed. */ + int closed; + /** Number of bytes written. */ + int written; + /** Number of bytes read. */ + int read; +}; + + +/** IOStream version of stdin. */ +extern IOStream *iostdin; + +/** IOStream version of stdout, */ +extern IOStream *iostdout; + +/** IOStream version of stderr. */ +extern IOStream *iostderr; + +extern int IOStream_print(IOStream *io, const char *format, ...); +extern int IOStream_vprint(IOStream *io, const char *format, va_list args); + +/** Read from a stream. + * + * @param stream input + * @param buf where to put input + * @param n number of bytes to read + * @return if ok, number of bytes read, otherwise negative error code + */ +static inline int IOStream_read(IOStream *stream, void *buf, size_t n){ + int result = 0; + if(stream->closed) goto exit; + if(!stream->methods || !stream->methods->read){ + result = -EINVAL; + goto exit; + } + result = stream->methods->read(stream, buf, n); + if(result > 0){ + stream->read += result; + } + exit: + return result; +} + +/** Write to a stream. + * + * @param stream input + * @param buf where to put input + * @param n number of bytes to write + * @return if ok, number of bytes read, otherwise negative error code + */ +static inline int IOStream_write(IOStream *stream, const void *buf, size_t n){ + int result = 0; + if(stream->closed) goto exit; + if(!stream->methods || !stream->methods->write){ + result = -EINVAL; + goto exit; + } + result = stream->methods->write(stream, buf, n); + if(result > 0){ + stream->written += result; + } + exit: + return result; +} + +/** Flush the stream. + * + * @param stream stream + * @return 0 on success, IOSTREAM_EOF otherwise + */ +static inline int IOStream_flush(IOStream *stream){ + int result = 0; + if(stream->closed){ + result = IOSTREAM_EOF; + } else if(stream->methods->flush){ + result = stream->methods->flush(stream); + if(result < 0) result = IOSTREAM_EOF; + } + return result; +} + +/** Check whether the stream has an error. + * + * @param stream to check + * @return 1 for error, 0 otherwise + */ +static inline int IOStream_error(IOStream *stream){ + int err = 0; + if(stream->methods && stream->methods->error){ + err = stream->methods->error(stream); + } + return err; +} + +/** Close the stream. + * + * @param stream to close + * @return 1 for error, 0 otherwise + */ +static inline int IOStream_close(IOStream *stream){ + int err = 1; + if(stream->methods && stream->methods->close){ + err = stream->methods->close(stream); + } + return err; +} + +/** Test if the stream has been closed. + * + * @param stream to check + * @return 1 if closed, 0 otherwise + */ +static inline int IOStream_is_closed(IOStream *stream){ + return stream->closed; +} + +/** Free the memory used by the stream. + * + * @param stream to free + */ +static inline void IOStream_free(IOStream *stream){ + if(stream->methods && stream->methods->free){ + stream->methods->free(stream); + } + *stream = (IOStream){}; + deallocate(stream); +} + + +/** Print a character to a stream, like fputc(). + * + * @param stream to print to + * @param c character to print + * @return result code from the print + */ +static inline int IOStream_putc(IOStream *stream, int c){ + int err; + unsigned char b = (unsigned char)c; + err = IOStream_write(stream, &b, 1); + if(err < 1){ + err = IOSTREAM_EOF; + } else { + err = b; + } + return err; +} + +/** Read from a stream, like fgetc(). + * + * @param stream to read from + * @return IOSTREAM_EOF on error, character read otherwise + */ +static inline int IOStream_getc(IOStream *stream){ + int err, rc; + unsigned char b; + + err = IOStream_read(stream, &b, 1); + if(err < 1){ + rc = IOSTREAM_EOF; + } else { + rc = b; + } + return rc; +} + +/** Get number of bytes read. + * + * @param stream to get from + * @return number of bytes read + */ +static inline int IOStream_get_read(IOStream *stream){ + return stream->read; +} + +/** Get number of bytes written. + * + * @param stream to get from + * @return number of bytes written + */ +static inline int IOStream_get_written(IOStream *stream){ + return stream->written; +} + + +#endif /* ! _XC_LINUX_SAVE_H_ */ diff --git a/tools/lib/kernel_stream.c b/tools/lib/kernel_stream.c new file mode 100644 index 0000000000..345b048015 --- /dev/null +++ b/tools/lib/kernel_stream.c @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2001 - 2004 Mike Wray + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/** @file + * An IOStream implementation using printk() for output. + * Input is not implemented. + */ +#ifdef __KERNEL__ + +#include +#include +#include +#include +#include +#include +#include + +#include "kernel_stream.h" +#include "allocate.h" + +/** Number of characters in the output buffer. + * The kernel uses 1024 for printk, so that should suffice. + */ +#define BUF_N 1024 + +/** State for a kernel stream. */ +typedef struct KernelData { + /** Stream lock. We need a lock to serialize access to the stream. */ + spinlock_t lock; + /** Saved flags for locking. */ + unsigned long flags; + /** Size of the output buffer. */ + int buf_n; + /** Output buffer. */ + char buf[BUF_N]; +} KernelData; + +static int kernel_write(IOStream *s, const char *msg, int n); +static void kernel_free(IOStream *s); +static void kernel_stream_lock(IOStream *s); +static void kernel_stream_unlock(IOStream *s); + +/** Methods for a kernel stream. Output only. */ +static const IOMethods kernel_methods = { + write: kernel_write, + free: kernel_free, + lock: kernel_stream_lock, + unlock: kernel_stream_unlock, +}; + +/** Shared state for kernel streams. + * All implementations write using printk, so we can use + * shared state and avoid allocating it. + */ +static const KernelData kernel_data = { + lock: SPIN_LOCK_UNLOCKED, + flags: 0, + buf_n: BUF_N, +}; + +/** Stream for kernel printk. */ +static IOStream iokernel = { + methods: &kernel_methods, + data: &kernel_data, +}; + +/** Stream for kernel printk. */ +IOStream *iostdout = &iokernel; + +/** Stream for kernel printk. */ +IOStream *iostdin = &iokernel; + +/** Stream for kernel printk. */ +IOStream *iostderr = &iokernel; + +/** Get an output-only stream implementation using + * printk(). The stream uses static storage, and must not be freed. + * + * @return kernel stream + */ +IOStream get_stream_kernel(void){ + return iokernel; +} + +/** Obtain the lock on the stream state. + * + * @param kdata stream state + */ +static inline void KernelData_lock(KernelData *kdata){ + spin_lock_irqsave(&kdata->lock, kdata->flags); +} + +/** Release the lock on the stream state. + * + * @param kdata stream state + */ +static inline void KernelData_unlock(KernelData *kdata){ + spin_unlock_irqrestore(&kdata->lock, kdata->flags); +} + +/** Get the stream state. + * + * @param s kernel stream + * @return stream state + */ +static inline KernelData *get_kernel_data(IOStream *s){ + return (KernelData*)s->data; +} + +/** Obtain the lock on the stream state. + * + * @param s stream + */ +void kernel_stream_lock(IOStream *s){ + KernelData_lock(get_kernel_data(s)); +} + +/** Release the lock on the stream state. + * + * @param s stream + */ +void kernel_stream_unlock(IOStream *s){ + KernelData_unlock(get_kernel_data(s)); +} + +/** Write to a kernel stream. + * + * @param stream kernel stream + * @param format print format + * @param args print arguments + * @return result of the print + */ +static int kernel_write(IOStream *stream, const char *buf, int n){ + KernelData *kdata = get_kernel_data(stream); + int k; + k = kdata->buf_n - 1; + if(n < k) k = n; + memcpy(kdata->buf, buf, k); + kdata->buf[k] = '\0' + printk(kdata->buf); + return k; +} + +/** Free a kernel stream. + * Frees the internal state of the stream. + * Do not call this unless the stream was dynamically allocated. + * Do not call this on a stream returned from get_stream_kernel(). + * + * @param io stream to free + */ +static void kernel_free(IOStream *io){ + KernelData *kdata; + if(io == &iokernel) return; + kdata = get_kernel_data(io); + zero(kdata, sizeof(*kdata)); + deallocate(kdata); +} +#endif /* __KERNEL__ */ + + + + diff --git a/tools/lib/kernel_stream.h b/tools/lib/kernel_stream.h new file mode 100644 index 0000000000..be370f2a45 --- /dev/null +++ b/tools/lib/kernel_stream.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2001 - 2004 Mike Wray + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _XEN_LIB_KERNEL_STREAM_H_ +#define _XEN_LIB_KERNEL_STREAM_H_ + +#ifdef __KERNEL__ +#include "iostream.h" + +extern IOStream get_stream_kernel(void); +#define get_stream_stdout get_stream_kernel + +#endif /* __KERNEL__ */ +#endif /* !_XEN_LIB_KERNEL_STREAM_H_ */ diff --git a/tools/lib/lexis.c b/tools/lib/lexis.c new file mode 100644 index 0000000000..26d2ec4d5b --- /dev/null +++ b/tools/lib/lexis.c @@ -0,0 +1,93 @@ +/* + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. This library is + * distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/** @file + * Lexical analysis. + */ + +#include "sys_string.h" +#include "lexis.h" +#include + +/** Check if a value lies in a (closed) range. + * + * @param x value to test + * @param lo low end of the range + * @param hi high end of the range + * @return 1 if x is in the interval [lo, hi], 0 otherwise + */ +inline static int in_range(int x, int lo, int hi){ + return (lo <= x) && (x <= hi); +} + +/** Determine if a string is an (unsigned) decimal number. + * + * @param s pointer to characters to test + * @param n length of string + * @return 1 if s is a decimal number, 0 otherwise. + */ +int is_decimal_number(const char *s, int n){ + int i; + if(n <= 0)return 0; + for(i = 0; i < n; i++){ + if(!in_decimal_digit_class(s[i])) return 0; + } + return 1; +} + +/** Determine if a string is a hex number. + * Hex numbers are 0, or start with 0x or 0X followed + * by a non-zero number of hex digits (0-9,a-f,A-F). + * + * @param s pointer to characters to test + * @param n length of string + * @return 1 if s is a hex number, 0 otherwise. + */ +int is_hex_number(const char *s, int n){ + int i; + if(n <= 0) return 0; + if(n == 1){ + return s[0]=='0'; + } + if(n <= 3) return 0; + if(s[0] != '0' || (s[1] != 'x' && s[1] != 'X')) return 0; + for(i = 2; i < n; i++){ + if(!in_hex_digit_class(s[i])) return 0; + } + return 1; +} + +/** Test if a string matches a keyword. + * The comparison is case-insensitive. + * The comparison fails if either argument is null. + * + * @param s string + * @param k keyword + * @return 1 if they match, 0 otherwise + */ +int is_keyword(const char *s, const char *k){ + return s && k && !strcasecmp(s, k); +} + +/** Test if a string matches a character. + * + * @param s string + * @param c character (non-null) + * @return 1 if s contains exactly c, 0 otherwise + */ +int is_keychar(const char *s, char c){ + return c && (s[0] == c) && !s[1]; +} diff --git a/tools/lib/lexis.h b/tools/lib/lexis.h new file mode 100644 index 0000000000..7d8fe7bc63 --- /dev/null +++ b/tools/lib/lexis.h @@ -0,0 +1,122 @@ +/* + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. This library is + * distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _SP_LEXIS_H_ +#define _SP_LEXIS_H_ + +#include "sys_string.h" +#include "sys_ctype.h" + +/** @file + * Lexical analysis. + */ + +/** Class of characters treated as space. */ +#define space_class ((char []){ '\n', '\r', '\t', ' ', '\f' , 0 }) + +/** Class of separator characters. */ +#define sep_class "{}()<>[]@!;" + +#define comment_class "#" + +/** Determine if a character is in a given class. + * + * @param c character to test + * @param s null-terminated string of characters in the class + * @return 1 if c is in the class, 0 otherwise. + */ +static inline int in_class(int c, const char *s){ + return s && (strchr(s, c) != 0); +} + +/** Determine if a character is in the space class. + * + * @param c character to test + * @return 1 if c is in the class, 0 otherwise. + */ +static inline int in_space_class(int c){ + return in_class(c, space_class); +} + +static inline int in_comment_class(int c){ + return in_class(c, comment_class); +} + +/** Determine if a character is in the separator class. + * Separator characters terminate tokens, and do not need space + * to separate them. + * + * @param c character to test + * @return 1 if c is in the class, 0 otherwise. + */ +static inline int in_sep_class(int c){ + return in_class(c, sep_class); +} + +/** Determine if a character is in the alpha class. + * + * @param c character to test + * @return 1 if c is in the class, 0 otherwise. + */ +static inline int in_alpha_class(int c){ + return isalpha(c); +} + +/** Determine if a character is in the octal digit class. + * + * @param c character to test + * @return 1 if c is in the class, 0 otherwise. + */ +static inline int in_octal_digit_class(int c){ + return '0' <= c && c <= '7'; +} + +/** Determine if a character is in the decimal digit class. + * + * @param c character to test + * @return 1 if c is in the class, 0 otherwise. + */ +static inline int in_decimal_digit_class(int c){ + return isdigit(c); +} + +/** Determine if a character is in the hex digit class. + * + * @param c character to test + * @return 1 if c is in the class, 0 otherwise. + */ +static inline int in_hex_digit_class(int c){ + return isdigit(c) || in_class(c, "abcdefABCDEF"); +} + + +static inline int in_string_quote_class(int c){ + return in_class(c, "'\""); +} + +static inline int in_printable_class(int c){ + return ('A' <= c && c <= 'Z') + || ('a' <= c && c <= 'z') + || ('0' <= c && c <= '9') + || in_class(c, "!$%&*+,-./:;<=>?@^_`{|}~"); +} + +extern int is_decimal_number(const char *s, int n); +extern int is_hex_number(const char *s, int n); +extern int is_keyword(const char *s, const char *k); +extern int is_keychar(const char *s, char c); + +#endif /* !_SP_LEXIS_H_ */ diff --git a/tools/lib/lzi_stream.c b/tools/lib/lzi_stream.c new file mode 100644 index 0000000000..0f09734201 --- /dev/null +++ b/tools/lib/lzi_stream.c @@ -0,0 +1,590 @@ +/* $Id: lzi_stream.c,v 1.4 2003/09/30 15:22:53 mjw Exp $ */ +#define __FILE_ID_INFO "$Id: lzi_stream.c,v 1.4 2003/09/30 15:22:53 mjw Exp $" +#include +static char __rcsid[] __attribute__((unused)) = WHAT_ID __FILE_ID_INFO; +/* + * Copyright (C) 2003 Hewlett-Packard Company. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/** @file + * An IOStream implementation using LZI to provide compression and decompression. + * This is designed to provide compression without output latency. + * Flushing an LZI stream flushes all pending data to the underlying stream. + * This is essential for stream-based (e.g. networked) applications. + * + * A compressed data stream is a sequence of blocks. + * Each block is the block size followed by the compressed data. + * The last block has size zero. + * Sizes are 4-byte unsigned in network order. + * + * This format allows compressed data to be read from a stream without reading + * past the logical end of compressed data. + * + * @author Mike Wray + */ +#ifndef __KERNEL__ + +#include +#include +#include +#include + +#include "zlib.h" + +#include "allocate.h" +#include "lzi_stream.h" +#include "file_stream.h" +#include "marshal.h" + +#define dprintf(fmt, args...) fprintf(stdout, "[DEBUG] LZI>%s" fmt, __FUNCTION__, ##args) +#define wprintf(fmt, args...) fprintf(stderr, "[WARN] LZI>%s" fmt, __FUNCTION__, ##args) +#define iprintf(fmt, args...) fprintf(stdout, "[INFO] LZI>%s" fmt, __FUNCTION__, ##args) +#define eprintf(fmt, args...) fprintf(stderr, "[ERROR] LZI>%s" fmt, __FUNCTION__, ##args) + +static int lzi_read(IOStream *s, void *buf, size_t size, size_t count); +static int lzi_write(IOStream *s, const void *buf, size_t size, size_t count); +static int lzi_print(IOStream *s, const char *msg, va_list args); +static int lzi_getc(IOStream *s); +static int lzi_error(IOStream *s); +static int lzi_close(IOStream *s); +static void lzi_free(IOStream *s); +static int lzi_flush(IOStream *s); + +enum { + LZI_WRITE = 1, + LZI_READ = 2, +}; + +/** Methods used by a gzFile* IOStream. */ +static const IOMethods lzi_methods = { + read: lzi_read, + write: lzi_write, + print: lzi_print, + getc: lzi_getc, + error: lzi_error, + close: lzi_close, + free: lzi_free, + flush: lzi_flush, +}; + +#define BUFFER_SIZE (512 * 1024) + +typedef struct LZIState { + z_stream zstream; + void *inbuf; + uint32_t inbuf_size; + void *outbuf; + uint32_t outbuf_size; + /** Underlying stream for I/O. */ + IOStream *io; + /** Flags. */ + int flags; + /** Error indicator. */ + int error; + int eof; + int plain_bytes; + int comp_bytes; + int zstream_initialized; + int flushed; +} LZIState; + +static inline int LZIState_writeable(LZIState *s){ + return (s->flags & LZI_WRITE) != 0; +} + +static inline int LZIState_readable(LZIState *s){ + return (s->flags & LZI_READ) != 0; +} + +void LZIState_free(LZIState *z){ + if(!z) return; + if(z->zstream_initialized){ + if(LZIState_writeable(z)){ + deflateEnd(&z->zstream); + } else if(LZIState_readable(z)){ + inflateEnd(&z->zstream); + } + } + deallocate(z->inbuf); + deallocate(z->outbuf); + deallocate(z); +} + +static int mode_flags(const char *mode, int *flags){ + int err = 0; + int r=0, w=0; + if(!mode){ + err = -EINVAL; + goto exit; + } + for(; *mode; mode++){ + if(*mode == 'w') w = 1; + if(*mode == 'r') r = 1; + } + if(r + w != 1){ + err = -EINVAL; + goto exit; + } + if(r) *flags |= LZI_READ; + if(w) *flags |= LZI_WRITE; + exit: + return err; +} + +/** Get the stream state. + * + * @param s lzi stream + * @return stream state. + */ +static inline LZIState * lzi_state(IOStream *io){ + return io->data; +} + +IOStream *lzi_stream_io(IOStream *io){ + LZIState *s = lzi_state(io); + return s->io; +} + +static inline void set_error(LZIState *s, int err){ + if(err < 0 && !s->error){ + s->error = err; + } +} + +static int zerror(LZIState *s, int err){ + if(err){ + //dprintf("> err=%d\n", err); + if(err < 0) set_error(s, -EIO); + } + return s->error; +} + +int lzi_stream_plain_bytes(IOStream *io){ + LZIState *s = lzi_state(io); + return s->plain_bytes; +} + +int lzi_stream_comp_bytes(IOStream *io){ + LZIState *s = lzi_state(io); + return s->comp_bytes; +} + +float lzi_stream_ratio(IOStream *io){ + LZIState *s = lzi_state(io); + float ratio = 0.0; + if(s->comp_bytes){ + ratio = ((float) s->comp_bytes)/((float) s->plain_bytes); + } + return ratio; +} + +static int alloc(void **p, int n){ + *p = allocate(n); + return (p ? 0 : -ENOMEM); +} + +LZIState * LZIState_new(IOStream *io, int flags){ + int err = -ENOMEM; + int zlevel = Z_BEST_SPEED; // Level 1 compression - fastest. + int zstrategy = Z_DEFAULT_STRATEGY; + int zwindow = MAX_WBITS; + int zmemory = 8; + LZIState *z = ALLOCATE(LZIState); + + //dprintf(">\n"); + if(!z) goto exit; + z->io = io; + z->flags = flags; + + if(LZIState_writeable(z)){ + z->outbuf_size = BUFFER_SIZE; + /* windowBits is passed < 0 to suppress zlib header */ + err = deflateInit2(&z->zstream, zlevel, Z_DEFLATED, -zwindow, zmemory, zstrategy); + if (err != Z_OK) goto exit; + z->zstream_initialized = 1; + err = alloc(&z->outbuf, z->outbuf_size); + if(err) goto exit; + z->zstream.next_out = z->outbuf; + z->zstream.avail_out = z->outbuf_size; + } else { + z->inbuf_size = BUFFER_SIZE; + err = alloc(&z->inbuf, z->inbuf_size); + if(err) goto exit; + ///z->zstream.next_in = z->inbuf; + + /* windowBits is passed < 0 to tell that there is no zlib header. + * Note that in this case inflate *requires* an extra "dummy" byte + * after the compressed stream in order to complete decompression and + * return Z_STREAM_END. Here the gzip CRC32 ensures that 4 bytes are + * present after the compressed stream. + */ + err = inflateInit2(&z->zstream, -zwindow); + if(err != Z_OK) goto exit; + z->zstream_initialized = 1; + } + + exit: + if(err){ + LZIState_free(z); + z = NULL; + } + //dprintf("< z=%p\n", z); + return z; +} + +int read_block(LZIState *s){ + int err = 0, k = 0; + //dprintf(">\n"); + if(s->eof) goto exit; + err = unmarshal_uint32(s->io, &k); + if(err) goto exit; + if(k > s->inbuf_size){ + err = -EINVAL; + goto exit; + } + if(k){ + err = unmarshal_bytes(s->io, s->inbuf, k); + if(err) goto exit; + } else { + s->eof = 1; + } + s->zstream.avail_in = k; + s->zstream.next_in = s->inbuf; + s->comp_bytes += 4; + s->comp_bytes += k; + exit: + //dprintf("< err=%d\n", err); + return err; +} + +int write_block(LZIState *s){ + int err = 0; + int k = ((char*)s->zstream.next_out) - ((char*)s->outbuf); + int k2 = s->outbuf_size - s->zstream.avail_out; + //dprintf("> k=%d k2=%d\n", k, k2); + if(!k) goto exit; + err = marshal_uint32(s->io, k); + if(err) goto exit; + err = marshal_bytes(s->io, s->outbuf, k); + if(err) goto exit; + s->zstream.next_out = s->outbuf; + s->zstream.avail_out = s->outbuf_size; + s->comp_bytes += 4; + s->comp_bytes += k; + exit: + //dprintf("< err=%d\n", err); + return err; +} + +int write_terminator(LZIState *s){ + int err = 0; + char c = 0; + err = marshal_uint32(s->io, 1); + if(err) goto exit; + err = marshal_bytes(s->io, &c, 1); + if(err) goto exit; + err = marshal_uint32(s->io, 0); + if(err) goto exit; + s->comp_bytes += 9; + exit: + return err; +} + +/** Write to the underlying stream using fwrite(); + * + * @param io destination + * @param buf data + * @param size size of data elements + * @param count number of data elements to write + * @return number of data elements written + */ +static int lzi_write(IOStream *io, const void *buf, size_t size, size_t count){ + int err = 0; + int n = size * count; + LZIState *s = lzi_state(io); + + //dprintf("> buf=%p size=%d count=%d n=%d\n", buf, size, count, n); + if(!LZIState_writeable(s)){ + err = -EINVAL; + goto exit; + } + s->flushed = 0; + s->zstream.next_in = (void*)buf; + s->zstream.avail_in = n; + while(s->zstream.avail_in){ + if(s->zstream.avail_out == 0){ + err = write_block(s); + if(err) goto exit; + } + //dprintf("> 1 deflate avail_in=%d avail_out=%d\n", s->zstream.avail_in, s->zstream.avail_out); + //dprintf("> 1 deflate next_in=%p next_out=%p\n", s->zstream.next_in, s->zstream.next_out); + err = zerror(s, deflate(&s->zstream, Z_NO_FLUSH)); + //dprintf("> 2 deflate avail_in=%d avail_out=%d\n", s->zstream.avail_in, s->zstream.avail_out); + //dprintf("> 2 deflate next_in=%p next_out=%p\n", s->zstream.next_in, s->zstream.next_out); + if(err) goto exit; + } + err = n; + s->plain_bytes += n; + if(size != 1) err /= size; + exit: + //dprintf("< err=%d\n", err); + return err; +} + + +/** Read from the underlying stream. + * + * @param io input + * @param buf where to put input + * @param size size of data elements + * @param count number of data elements to read + * @return number of data elements read + */ +static int lzi_read(IOStream *io, void *buf, size_t size, size_t count){ + int err, zerr; + int n = size * count; + LZIState *s = lzi_state(io); + + //dprintf("> size=%d count=%d n=%d\n", size, count, n); + if(!LZIState_readable(s)){ + err = -EINVAL; + goto exit; + } + s->zstream.next_out = buf; + s->zstream.avail_out = n; + while(s->zstream.avail_out){ + if(s->zstream.avail_in == 0){ + err = read_block(s); + } + //dprintf("> 1 deflate avail_in=%d avail_out=%d\n", s->zstream.avail_in, s->zstream.avail_out); + zerr = inflate(&s->zstream, Z_NO_FLUSH); + //dprintf("> 2 deflate avail_in=%d avail_out=%d\n", s->zstream.avail_in, s->zstream.avail_out); + if(zerr == Z_STREAM_END) break; + //dprintf("> zerr=%d\n", zerr); + err = zerror(s, zerr); + if(err) goto exit; + } + err = n - s->zstream.avail_out; + s->plain_bytes += err; + if(size != 1) err /= size; + exit: + set_error(s, err); + //dprintf("< err=%d\n", err); + return err; +} + +/** Print to the underlying stream. + * Returns 0 if the formatted output is too big for the internal buffer. + * + * @param io lzi stream + * @param msg format to use + * @param args arguments + * @return result of the print + */ +static int lzi_print(IOStream *io, const char *msg, va_list args){ + char buf[1024]; + int buf_n = sizeof(buf); + int n; + LZIState *s = lzi_state(io); + if(!LZIState_writeable(s)){ + n = -EINVAL; + goto exit; + } + n = vsnprintf(buf, buf_n, (char*)msg, args); + if(n < 0) goto exit; + if(n > buf_n){ + n = 0; + } else { + n = lzi_write(io, buf, 1, n); + } + exit: + return n; +} + +/** Read a character from the underlying stream + * + * @param io lzi stream + * @return character read, IOSTREAM_EOF on end of file (or error) + */ +static int lzi_getc(IOStream *io){ + int err; + char c; + err = lzi_read(io, &c, 1, 1); + if(err < 1) c = EOF; + err = (c==EOF ? IOSTREAM_EOF : c); + return err; +} + +static int flush_output(LZIState *s, int mode){ + int err = 0, zerr; + int done = 0; + int avail_out_old; + int count = 10; + + //dprintf("> avail_in=%d avail_out=%d\n", s->zstream.avail_in, s->zstream.avail_out); + if(s->flushed == 1 + mode) goto exit; + //s->zstream.avail_in = 0; /* should be zero already anyway */ + for(;;){ + // Write any available output. + if(done || s->zstream.avail_out == 0){ + err = write_block(s); + if(err) goto exit; + if(done) break; + } + //dprintf("> 1 deflate avail_in=%d avail_out=%d\n", s->zstream.avail_in, s->zstream.avail_out); + avail_out_old = s->zstream.avail_out; + zerr = deflate(&s->zstream, mode); + err = zerror(s, zerr); + //dprintf("> 2 deflate avail_in=%d avail_out=%d\n", s->zstream.avail_in, s->zstream.avail_out); + //dprintf("> deflate=%d\n", err); + //done = (s->zstream.avail_out != 0); + //done = (s->zstream.avail_in == 0) && (s->zstream.avail_out == avail_out_old); + if(0 && mode == Z_FINISH){ + done = (zerr == Z_STREAM_END); + } else { + done = (s->zstream.avail_in == 0) + //&& (s->zstream.avail_out == avail_out_old) + && (s->zstream.avail_out != 0); + } + } + s->flushed = 1 + mode; + exit: + //dprintf("< err=%d\n", err); + return err; +} + +/** Flush any pending input to the underlying stream. + * + * @param s lzi stream + * @return 0 on success, error code otherwise + */ +static int lzi_flush(IOStream *io){ + int err = 0; + LZIState *s = lzi_state(io); + //dprintf(">\n"); + if(!LZIState_writeable(s)){ + err = -EINVAL; + goto exit; + } + err = flush_output(s, Z_SYNC_FLUSH); + if(err) goto exit; + err = IOStream_flush(s->io); + exit: + set_error(s, err); + //dprintf("< err=%d\n", err); + return (err < 0 ? err : 0); +} + +/** Check if a stream has an error. + * + * @param s lzi stream + * @return code if has an error, 0 otherwise + */ +static int lzi_error(IOStream *s){ + int err = 0; + LZIState *state = lzi_state(s); + err = state->error; + if(err) goto exit; + err = IOStream_error(state->io); + exit: + return err; +} + +/** Close an lzi stream. + * + * @param s lzi stream to close + * @return result of the close + */ +static int lzi_close(IOStream *io){ + int err = 0; + LZIState *s = lzi_state(io); + if(LZIState_writeable(s)){ + err = flush_output(s, Z_FINISH); + if(err) goto exit; + err = write_terminator(s); + if(err) goto exit; + err = IOStream_flush(s->io); + } + exit: + err = IOStream_close(s->io); + set_error(s, err); + return err; +} + +/** Free an lzi stream. + * + * @param s lzi stream + */ +static void lzi_free(IOStream *s){ + LZIState *state = lzi_state(s); + IOStream_free(state->io); + LZIState_free(state); + s->data = NULL; +} + +/** Create an lzi stream for an IOStream. + * + * @param io stream to wrap + * @return new IOStream using f for i/o + */ +IOStream *lzi_stream_new(IOStream *io, const char *mode){ + int err = -ENOMEM; + int flags = 0; + IOStream *zio = NULL; + LZIState *state = NULL; + + zio = ALLOCATE(IOStream); + if(!zio) goto exit; + err = mode_flags(mode, &flags); + if(err) goto exit; + state = LZIState_new(io, flags); + if(!state) goto exit; + err = 0; + zio->data = state; + zio->methods = &lzi_methods; + exit: + if(err){ + if(state) LZIState_free(state); + if(zio) deallocate(zio); + zio = NULL; + } + return zio; +} + +/** IOStream version of fdopen(). + * + * @param fd file descriptor + * @param flags giving the mode to open in (as for fdopen()) + * @return new stream for the open file, or NULL if failed + */ +IOStream *lzi_stream_fdopen(int fd, const char *mode){ + int err = -ENOMEM; + IOStream *io = NULL, *zio = NULL; + io = file_stream_fdopen(fd, mode); + if(!io) goto exit; + zio = lzi_stream_new(io, mode); + if(!io) goto exit; + err = 0; + exit: + if(err){ + IOStream_free(io); + IOStream_free(zio); + zio = NULL; + } + return zio; +} +#endif diff --git a/tools/lib/lzi_stream.h b/tools/lib/lzi_stream.h new file mode 100644 index 0000000000..0ad4f8db8e --- /dev/null +++ b/tools/lib/lzi_stream.h @@ -0,0 +1,36 @@ +#/* $Id: lzi_stream.h,v 1.3 2003/09/30 15:22:53 mjw Exp $ */ +/* + * Copyright (C) 2003 Hewlett-Packard Company. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _SP_LZI_STREAM_H_ +#define _SP_LZI_STREAM_H_ + +#ifndef __KERNEL__ +#include "iostream.h" + +extern IOStream *lzi_stream_new(IOStream *io, const char *mode); +extern IOStream *lzi_stream_fopen(const char *file, const char *mode); +extern IOStream *lzi_stream_fdopen(int fd, const char *mode); +extern IOStream *lzi_stream_io(IOStream *zio); + +extern int lzi_stream_plain_bytes(IOStream *io); +extern int lzi_stream_comp_bytes(IOStream *io); +extern float lzi_stream_ratio(IOStream *io); + +#endif +#endif /* !_SP_FILE_STREAM_H_ */ diff --git a/tools/lib/lzo_stream.c b/tools/lib/lzo_stream.c new file mode 100644 index 0000000000..bf7c348471 --- /dev/null +++ b/tools/lib/lzo_stream.c @@ -0,0 +1,596 @@ +/* $Id: lzo_stream.c,v 1.4 2003/09/30 15:22:53 mjw Exp $ */ +#define __FILE_ID_INFO "$Id: lzo_stream.c,v 1.4 2003/09/30 15:22:53 mjw Exp $" +#include +static char __rcsid[] __attribute__((unused)) = WHAT_ID __FILE_ID_INFO; +/* + * Copyright (C) 2003 Hewlett-Packard Company. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/** @file + * An IOStream implementation using LZO to provide compression and decompression. + * This is designed to provide reasonable compression without output latency. + * Flushing an LZO stream flushes all pending data to the underlying stream. + * This is essential for stream-based (e.g. networked) applications. + * + * A compressed data stream is a sequence of blocks. + * Each block except the last is the plain data size followed by the compressed data size + * and the compressed data. The last block has plain data size zero and omits the rest. + * Sizes are 4-byte unsigned in network order. If the compressed size is smaller than + * the plain size the block data is compressed, otherwise it is plain (uncompressed). + * + * This format allows compressed data to be read from a stream without reading + * past the logical end of compressed data. + * + * @author Mike Wray + */ +#ifndef __KERNEL__ + +#include +#include +#include +#include + +#include "lzo1x.h" + +#include "allocate.h" +#include "lzo_stream.h" +#include "file_stream.h" +#include "marshal.h" + +#define dprintf(fmt, args...) fprintf(stdout, "[DEBUG] LZO>%s" fmt, __FUNCTION__, ##args) +#define wprintf(fmt, args...) fprintf(stderr, "[WARN] LZO>%s" fmt, __FUNCTION__, ##args) +#define iprintf(fmt, args...) fprintf(stdout, "[INFO] LZO>%s" fmt, __FUNCTION__, ##args) +#define eprintf(fmt, args...) fprintf(stderr, "[ERROR] LZO>%s" fmt, __FUNCTION__, ##args) + +static int lzo_read(IOStream *s, void *buf, size_t size, size_t count); +static int lzo_write(IOStream *s, const void *buf, size_t size, size_t count); +static int lzo_print(IOStream *s, const char *msg, va_list args); +static int lzo_getc(IOStream *s); +static int lzo_error(IOStream *s); +static int lzo_close(IOStream *s); +static void lzo_free(IOStream *s); +static int lzo_flush(IOStream *s); + +enum { + LZO_WRITE = 1, + LZO_READ = 2, +}; + +/** Methods used by a gzFile* IOStream. */ +static const IOMethods lzo_methods = { + read: lzo_read, + write: lzo_write, + print: lzo_print, + getc: lzo_getc, + error: lzo_error, + close: lzo_close, + free: lzo_free, + flush: lzo_flush, +}; + +//#define PLAIN_SIZE (64 * 1024) +//#define PLAIN_SIZE (128 * 1024) +#define PLAIN_SIZE (512 * 1024) + +//#define NOCOMPRESS + +typedef struct LZOState { + /** Flags. */ + int flags; + /** Error indicator. */ + int error; + /** Underlying stream for I/O. */ + IOStream *io; + /** Working memory (only needed for compression, not decompression). */ + lzo_byte *memory; + /** Buffer for plain (uncompressed) data. */ + lzo_byte *plain; + /** Size of the plain buffer. */ + lzo_uint plain_size; + /** Pointer into the plain buffer. */ + lzo_byte *plain_ptr; + /** Number of bytes of plain data available. */ + lzo_uint plain_n; + /** Buffer for compressed data. */ + lzo_byte *comp; + /** Size of the compressed buffer. */ + lzo_uint comp_size; + + int plain_bytes; + int comp_bytes; +} LZOState; + +void LZOState_free(LZOState *z){ + if(!z) return; + deallocate(z->memory); + deallocate(z->plain); + deallocate(z->comp); + deallocate(z); +} + +/** Maximum size of compressed data for the given plain data size. + * + * @param plain_size size of plain data + * @return maximum size of compressed data + */ +static int comp_size(int plain_size){ + return plain_size + (plain_size / 64) + 16 + 3; +} + +static int mode_flags(const char *mode, int *flags){ + int err = 0; + int r=0, w=0; + if(!mode){ + err = -EINVAL; + goto exit; + } + for(; *mode; mode++){ + if(*mode == 'w') w = 1; + if(*mode == 'r') r = 1; + } + if(r + w != 1){ + err = -EINVAL; + goto exit; + } + if(r) *flags |= LZO_READ; + if(w) *flags |= LZO_WRITE; + exit: + return err; +} + +/** Get the stream state. + * + * @param s lzo stream + * @return stream state. + */ +static inline LZOState * lzo_state(IOStream *s){ + return s->data; +} + +IOStream *lzo_stream_io(IOStream *s){ + LZOState *state = lzo_state(s); + return state->io; +} + +static inline void set_error(LZOState *state, int err){ + if(err < 0 && !state->error){ + state->error = err; + } +} + +int lzo_stream_plain_bytes(IOStream *s){ + LZOState *state = lzo_state(s); + return state->plain_bytes; +} + +int lzo_stream_comp_bytes(IOStream *s){ + LZOState *state = lzo_state(s); + return state->comp_bytes; +} + +float lzo_stream_ratio(IOStream *s){ + LZOState *state = lzo_state(s); + float ratio = 0.0; + if(state->comp_bytes){ + ratio = ((float) state->comp_bytes)/((float) state->plain_bytes); + } + return ratio; +} + +static inline int LZOState_writeable(LZOState *state){ + return (state->flags & LZO_WRITE) != 0; +} + +static inline int LZOState_readable(LZOState *state){ + return (state->flags & LZO_READ) != 0; +} + +LZOState * LZOState_new(IOStream *io, int flags){ + int err = -ENOMEM; + LZOState *z = ALLOCATE(LZOState); + //dprintf(">\n"); + if(!z) goto exit; + z->io = io; + z->flags = flags; + if(LZOState_writeable(z)){ + z->memory = allocate(LZO1X_1_MEM_COMPRESS); + if(!z->memory) goto exit; + } + z->plain_size = PLAIN_SIZE; + z->plain = allocate(z->plain_size); + if(!z->plain) goto exit; + z->plain_ptr = z->plain; + z->comp_size = comp_size(z->plain_size); + z->comp = allocate(z->comp_size); + if(!z->comp) goto exit; + err = 0; + exit: + if(err){ + LZOState_free(z); + z = NULL; + } + //dprintf("< z=%p\n", z); + return z; +} + +static int lzo_compress(LZOState *state){ + int err = 0; + int k, comp_n; + //dprintf(">\n"); + //dprintf(">plain=%p plain_n=%d comp=%p memory=%p\n", state->plain, state->plain_n, state->comp, state->memory); + // Compress the plain buffer. + err = lzo1x_1_compress(state->plain, state->plain_n, + state->comp, &comp_n, + state->memory); + //dprintf("> err=%d plain_n=%d comp_n=%d\n", err, state->plain_n, comp_n); + // Write plain size, compressed size. + err = marshal_uint32(state->io, state->plain_n); + if(err) goto exit; + err = marshal_uint32(state->io, comp_n); + if(err) goto exit; + //dprintf("> write data...\n"); + // Write the smaller of the compressed and plain data. + if(state->plain_n < comp_n){ + k = state->plain_n; + err = marshal_bytes(state->io, state->plain, state->plain_n); + } else { + k = comp_n; + err = marshal_bytes(state->io, state->comp, comp_n); + } + if(err) goto exit; + // Total output bytes. + k+= 8; + //dprintf("> wrote %d bytes\n", k); + state->plain_bytes += state->plain_n; + state->comp_bytes += k; + //dprintf("> plain=%d, comp=%d, ratio=%3.2f\n", + // state->plain_bytes, state->comp_bytes, + // ((float)state->comp_bytes)/((float)state->plain_bytes)); + // Reset the plain buffer. + state->plain_ptr = state->plain; + state->plain_n = 0; + err = k; + exit: + //dprintf("< err=%d\n", err); + return err; +} + +static int lzo_decompress(LZOState *state){ + int plain_n, comp_n; + int err, k; + //dprintf(">\n"); + err = unmarshal_uint32(state->io, &plain_n); + //dprintf("> err=%d plain_n=%d\n", err, plain_n); + if(err) goto exit; + state->comp_bytes += 4; + if(plain_n == 0) goto exit; + err = unmarshal_uint32(state->io, &comp_n); + //dprintf("> err=%d comp_n=%d\n", err, comp_n); + if(err) goto exit; + state->comp_bytes += 4; + if(plain_n > state->plain_size){ + err = -EINVAL; + goto exit; + } + if(comp_n > plain_n){ + //dprintf("> reading plain data %d...\n", plain_n); + k = plain_n; + err = unmarshal_bytes(state->io, state->plain, plain_n); + state->plain_n = plain_n; + } else { + //dprintf("> reading comp data %d...\n", comp_n); + k = comp_n; + err = unmarshal_bytes(state->io, state->comp, comp_n); + //dprintf("> decompress comp_n=%d\n", comp_n); + err = lzo1x_decompress(state->comp, comp_n, + state->plain, &state->plain_n, + state->memory); + //dprintf("> err=%d plain=%d state->plain_n=%d\n", err, plain_n, state->plain_n); + if(err != LZO_E_OK || state->plain_n != plain_n){ + // Bad. Corrupted input. + err = -EINVAL; + eprintf("> Corrupted!\n"); + goto exit; + } + } + state->comp_bytes += k; + state->plain_bytes += state->plain_n; + state->plain_ptr = state->plain; + err = k; + exit: + //dprintf("< err=%d\n", err); + return err; +} + +/** Write to the underlying stream using fwrite(); + * + * @param stream destination + * @param buf data + * @param size size of data elements + * @param count number of data elements to write + * @return number of data elements written + */ +static int lzo_write(IOStream *s, const void *buf, size_t size, size_t count){ + int err = 0; + int n = size * count; // Total number of bytes to write. + int chunk; // Size of chunk to write. + int remaining; // Number of bytes remaining to write. + int space; // Amount of space left in plain buffer. + LZOState *state = lzo_state(s); +#ifdef NOCOMPRESS + //dprintf("> buf=%p size=%d count=%d\n", buf, size, count); + err = IOStream_write(state->io, buf, size, count); + //dprintf("< err=%d\n", err); +#else + //dprintf("> buf=%p size=%d count=%d n=%d\n", buf, size, count, n); + remaining = n; + space = state->plain_size - state->plain_n; + //dprintf("> plain=%p plain_ptr=%p plain_n=%d space=%d\n", + // state->plain, state->plain_ptr, state->plain_n, space); + while(remaining){ + chunk = remaining; + if(chunk > space) chunk = space; + //dprintf("> memcpy %p %p %d\n", state->plain_ptr, buf, chunk); + memcpy(state->plain_ptr, buf, chunk); + remaining -= chunk; + space -= chunk; + state->plain_ptr += chunk; + state->plain_n += chunk; + if(space == 0){ + // Input buffer is full. Compress and write it. + err = lzo_compress(state); + if(err < 0) goto exit; + space = state->plain_size - state->plain_n; + } + } + err = (size > 1 ? n / size : n); + exit: + set_error(state, err); +#endif + return err; +} + + +/** Read from the underlying stream. + * + * @param stream input + * @param buf where to put input + * @param size size of data elements + * @param count number of data elements to read + * @return number of data elements read + */ +static int lzo_read(IOStream *s, void *buf, size_t size, size_t count){ + int err = 0; + int k = 0; // Number of (plain) bytes read. + int remaining = size * count; // Number of bytes remaining to read. + int chunk; // Size of chunk to read. + LZOState *state = lzo_state(s); +#ifdef NOCOMPRESS + //dprintf("> buf=%p size=%d count=%d\n", buf, size, count); + err = IOStream_read(state->io, buf, size, count); + //dprintf("< err=%d\n", err); +#else + if(!(state->flags & LZO_READ)){ + err = -EINVAL; + goto exit; + } + while(remaining){ + if(state->plain_n == 0){ + // No more plain input, decompress some more. + err = lzo_decompress(state); + if(err < 0) goto exit; + // Stop reading if there is no more input. + if(err == 0 || state->plain_n == 0) break; + } + chunk = remaining; + if(chunk > state->plain_n) chunk = state->plain_n; + memcpy(buf, state->plain_ptr, chunk); + k += chunk; + buf += chunk; + state->plain_ptr += chunk; + state->plain_n -= chunk; + remaining -= chunk; + } + err = k; + exit: + set_error(state, err); +#endif + return err; +} + +/** Print to the underlying stream. + * Returns 0 if the formatted output is too big for the internal buffer. + * + * @param s lzo stream + * @param msg format to use + * @param args arguments + * @return result of the print + */ +static int lzo_print(IOStream *s, const char *msg, va_list args){ + char buf[1024]; + int buf_n = sizeof(buf); + int n; + LZOState *state = lzo_state(s); + if(!LZOState_writeable(state)){ + n = -EINVAL; + goto exit; + } + n = vsnprintf(buf, buf_n, (char*)msg, args); + if(n < 0) goto exit; + if(n > buf_n){ + n = 0; + } else { + n = lzo_write(s, buf, 1, n); + } + exit: + return n; +} + +/** Read a character from the underlying stream + * + * @param s lzo stream + * @return character read, IOSTREAM_EOF on end of file (or error) + */ +static int lzo_getc(IOStream *s){ + int err; + char c; + err = lzo_read(s, &c, 1, 1); + if(err < 1) c = EOF; + err = (c==EOF ? IOSTREAM_EOF : c); + return err; +} + +/** Flush any pending input to the underlying stream. + * + * @param s lzo stream + * @return 0 on success, error code otherwise + */ +static int lzo_flush(IOStream *s){ + int err = 0; + LZOState *state = lzo_state(s); + //dprintf(">\n"); +#ifdef NOCOMPRESS + err = IOStream_flush(state->io); +#else + if(!LZOState_writeable(state)){ + err = -EINVAL; + goto exit; + } + if(state->plain_n){ + err = lzo_compress(state); + if(err < 0) goto exit; + } + err = IOStream_flush(state->io); + exit: + set_error(state, err); +#endif + //dprintf("< err=%d\n", err); + return (err < 0 ? err : 0); +} + +/** Check if a stream has an error. + * + * @param s lzo stream + * @return code if has an error, 0 otherwise + */ +static int lzo_error(IOStream *s){ + int err = 0; + LZOState *state = lzo_state(s); + err = state->error; + if(err) goto exit; + err = IOStream_error(state->io); + exit: + return err; +} + +int lzo_stream_finish(IOStream *s){ + int err = 0; + LZOState *state = lzo_state(s); + if(!LZOState_writeable(state)){ + err = -EINVAL; + goto exit; + } + err = lzo_flush(s); + if(err < 0) goto exit; + err = marshal_int32(state->io, 0); + exit: + return err; +} + +/** Close an lzo stream. + * + * @param s lzo stream to close + * @return result of the close + */ +static int lzo_close(IOStream *s){ + int err = 0; + LZOState *state = lzo_state(s); +#ifdef NOCOMPRESS + err = IOStream_close(state->io); +#else + if(LZOState_writeable(state)){ + err = lzo_stream_finish(s); + } + err = IOStream_close(state->io); + set_error(state, err); +#endif + return err; +} + +/** Free an lzo stream. + * + * @param s lzo stream + */ +static void lzo_free(IOStream *s){ + LZOState *state = lzo_state(s); + IOStream_free(state->io); + LZOState_free(state); + s->data = NULL; +} + +/** Create an lzo stream for an IOStream. + * + * @param io stream to wrap + * @return new IOStream using f for i/o + */ +IOStream *lzo_stream_new(IOStream *io, const char *mode){ + int err = -ENOMEM; + int flags = 0; + IOStream *zio = NULL; + LZOState *state = NULL; + + zio = ALLOCATE(IOStream); + if(!zio) goto exit; + err = mode_flags(mode, &flags); + if(err) goto exit; + state = LZOState_new(io, flags); + if(!state) goto exit; + err = 0; + zio->data = state; + zio->methods = &lzo_methods; + exit: + if(err){ + if(state) LZOState_free(state); + if(zio) deallocate(zio); + zio = NULL; + } + return zio; +} + +/** IOStream version of fdopen(). + * + * @param fd file descriptor + * @param flags giving the mode to open in (as for fdopen()) + * @return new stream for the open file, or NULL if failed + */ +IOStream *lzo_stream_fdopen(int fd, const char *mode){ + int err = -ENOMEM; + IOStream *io = NULL, *zio = NULL; + io = file_stream_fdopen(fd, mode); + if(!io) goto exit; + zio = lzo_stream_new(io, mode); + if(!io) goto exit; + err = 0; + exit: + if(err){ + IOStream_free(io); + IOStream_free(zio); + zio = NULL; + } + return zio; +} +#endif diff --git a/tools/lib/lzo_stream.h b/tools/lib/lzo_stream.h new file mode 100644 index 0000000000..493da7b1ad --- /dev/null +++ b/tools/lib/lzo_stream.h @@ -0,0 +1,36 @@ +#/* $Id: lzo_stream.h,v 1.3 2003/09/30 15:22:53 mjw Exp $ */ +/* + * Copyright (C) 2003 Hewlett-Packard Company. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _SP_LZO_STREAM_H_ +#define _SP_LZO_STREAM_H_ + +#ifndef __KERNEL__ +#include "iostream.h" + +extern IOStream *lzo_stream_new(IOStream *io, const char *mode); +extern IOStream *lzo_stream_fopen(const char *file, const char *mode); +extern IOStream *lzo_stream_fdopen(int fd, const char *mode); +extern IOStream *lzo_stream_io(IOStream *zio); + +extern int lzo_stream_plain_bytes(IOStream *io); +extern int lzo_stream_comp_bytes(IOStream *io); +extern float lzo_stream_ratio(IOStream *io); + +#endif +#endif /* !_SP_FILE_STREAM_H_ */ diff --git a/tools/lib/marshal.c b/tools/lib/marshal.c new file mode 100644 index 0000000000..21691d4412 --- /dev/null +++ b/tools/lib/marshal.c @@ -0,0 +1,207 @@ +#include +#include "sys_net.h" +#include "allocate.h" +#include "marshal.h" + +#define dprintf(fmt, args...) IOStream_print(iostdout, "[DEBUG] %s" fmt, __FUNCTION__, ##args) +#define wprintf(fmt, args...) IOStream_print(iostderr, "[WARN] %s" fmt, __FUNCTION__, ##args) +#define iprintf(fmt, args...) IOStream_print(iostdout, "[INFO] %s" fmt, __FUNCTION__, ##args) +#define eprintf(fmt, args...) IOStream_print(iostderr, "[ERROR] %s" fmt, __FUNCTION__, ##args) + + +#define ARRAY_SIZE(ary) (sizeof(ary)/sizeof((ary)[0])) + +/* Messages are coded as msgid followed by message fields. + * Initial message on any channel is hello - so can check version + * compatibility. + * + * char* -> uint16_t:n + * ints/uints go as suitable number of bytes (e.g. uint16_t is 2 bytes). + * optional fields go as '1' or '0' (the 0/1 is 1 byte). + * lists go as ('1' )* '0' + */ + +int marshal_flush(IOStream *io){ + int err = 0; + err = IOStream_flush(io); + return err; +} + +int marshal_bytes(IOStream *io, void *s, uint32_t s_n){ + int err = 0; + int n; + n = IOStream_write(io, s, s_n); + if(n < 0){ + err = n; + } else if (n < s_n){ + wprintf("> Wanted %d, got %d\n", s_n, n); + err = -EIO; + } + return err; +} + +int unmarshal_bytes(IOStream *io, void *s, uint32_t s_n){ + int err = 0; + int n; + //dprintf("> s_n=%d\n", s_n); + n = IOStream_read(io, s, s_n); + //dprintf("> n=%d\n", n); + if(n < 0){ + err = n; + } else if(n < s_n){ + wprintf("> Wanted %d, got %d\n", s_n, n); + err = -EIO; + } + //dprintf("< err=%d\n", err); + return err; +} + +int marshal_uint8(IOStream *io, uint8_t x){ + return marshal_bytes(io, &x, sizeof(x)); +} + +int unmarshal_uint8(IOStream *io, uint8_t *x){ + return unmarshal_bytes(io, x, sizeof(*x)); +} + +int marshal_uint16(IOStream *io, uint16_t x){ + x = htons(x); + return marshal_bytes(io, &x, sizeof(x)); +} + +int unmarshal_uint16(IOStream *io, uint16_t *x){ + int err = 0; + err = unmarshal_bytes(io, x, sizeof(*x)); + *x = ntohs(*x); + return err; +} + +int marshal_int32(IOStream *io, int32_t x){ + int err = 0; + //dprintf("> x=%d\n", x); + x = htonl(x); + err = marshal_bytes(io, &x, sizeof(x)); + //dprintf("< err=%d\n", err); + return err; +} + +int unmarshal_int32(IOStream *io, int32_t *x){ + int err = 0; + //dprintf(">\n"); + err = unmarshal_bytes(io, x, sizeof(*x)); + *x = ntohl(*x); + //dprintf("< err=%d x=%d\n", err, *x); + return err; +} + +int marshal_uint32(IOStream *io, uint32_t x){ + int err = 0; + //dprintf("> x=%u\n", x); + x = htonl(x); + err = marshal_bytes(io, &x, sizeof(x)); + //dprintf("< err=%d\n", err); + return err; +} + +int unmarshal_uint32(IOStream *io, uint32_t *x){ + int err = 0; + //dprintf(">\n"); + err = unmarshal_bytes(io, x, sizeof(*x)); + *x = ntohl(*x); + //dprintf("< err=%d x=%u\n", err, *x); + return err; +} + +int marshal_uint64(IOStream *io, uint64_t x){ + int err; + err = marshal_uint32(io, (uint32_t) ((x >> 32) & 0xffffffff)); + if(err) goto exit; + err = marshal_uint32(io, (uint32_t) ( x & 0xffffffff)); + exit: + return err; +} + +int unmarshal_uint64(IOStream *io, uint64_t *x){ + int err = 0; + uint32_t hi, lo; + err = unmarshal_uint32(io, &hi); + if(err) goto exit; + err = unmarshal_uint32(io, &lo); + *x = (((uint64_t) hi) << 32) | lo; + exit: + return err; +} + +int marshal_net16(IOStream *io, net16_t x){ + return marshal_bytes(io, &x, sizeof(x)); +} + +int unmarshal_net16(IOStream *io, net16_t *x){ + int err = 0; + err = unmarshal_bytes(io, x, sizeof(*x)); + return err; +} + +int marshal_net32(IOStream *io, net32_t x){ + return marshal_bytes(io, &x, sizeof(x)); +} + +int unmarshal_net32(IOStream *io, net32_t *x){ + int err = 0; + err = unmarshal_bytes(io, x, sizeof(*x)); + return err; +} + +int marshal_string(IOStream *io, char *s, uint32_t s_n){ + int err; + //dprintf("> s=%s\n", s); + err = marshal_uint32(io, s_n); + if(err) goto exit; + err = marshal_bytes(io, s, s_n); + exit: + //dprintf("< err=%d\n", err); + return err; +} + +int unmarshal_string(IOStream *io, char *s, uint32_t s_n){ + int err = 0, val_n = 0; + //dprintf(">\n"); + err = unmarshal_uint32(io, &val_n); + if(err) goto exit; + if(val_n >= s_n){ + err = -EINVAL; + goto exit; + } + err = unmarshal_bytes(io, s, val_n); + if(err) goto exit; + s[val_n] = '\0'; + exit: + //dprintf("< err=%d s=%s\n", err, s); + return err; +} + +int unmarshal_new_string(IOStream *io, char **s, uint32_t *s_n){ + int err = 0, val_n = 0; + char *val = NULL; + //dprintf(">\n"); + err = unmarshal_uint32(io, &val_n); + if(err) goto exit; + val = allocate(val_n + 1); + if(!val){ + err = -ENOMEM; + goto exit; + } + err = unmarshal_bytes(io, val, val_n); + if(err) goto exit; + val[val_n] = '\0'; + exit: + if(err){ + if(val) deallocate(val); + val = NULL; + val_n = 0; + } + *s = val; + if(s_n) *s_n = val_n; + //dprintf("< err=%d s=%s\n", err, *s); + return err; +} diff --git a/tools/lib/marshal.h b/tools/lib/marshal.h new file mode 100644 index 0000000000..9a9d465b9b --- /dev/null +++ b/tools/lib/marshal.h @@ -0,0 +1,43 @@ +/* $Id: marshal.h,v 1.1 2003/10/17 15:48:43 mjw Exp $ */ +#ifndef _SP_MARSHAL_H_ +#define _SP_MARSHAL_H_ + +#include "iostream.h" + +/** A 16-bit uint in network order, e.g. a port number. */ +typedef uint16_t net16_t; + +/** A 32-bit uint in network order, e.g. an IP address. */ +typedef uint32_t net32_t; + +extern int marshal_flush(IOStream *io); + +extern int marshal_bytes(IOStream *io, void *s, uint32_t s_n); +extern int unmarshal_bytes(IOStream *io, void *s, uint32_t s_n); + +extern int marshal_uint8(IOStream *io, uint8_t x); +extern int unmarshal_uint8(IOStream *io, uint8_t *x); + +extern int marshal_uint16(IOStream *io, uint16_t x); +extern int unmarshal_uint16(IOStream *io, uint16_t *x); + +extern int marshal_uint32(IOStream *io, uint32_t x); +extern int unmarshal_uint32(IOStream *io, uint32_t *x); + +extern int marshal_int32(IOStream *io, int32_t x); +extern int unmarshal_int32(IOStream *io, int32_t *x); + +extern int marshal_uint64(IOStream *io, uint64_t x); +extern int unmarshal_uint64(IOStream *io, uint64_t *x); + +extern int marshal_net16(IOStream *io, net16_t x); +extern int unmarshal_net16(IOStream *io, net16_t *x); + +extern int marshal_net32(IOStream *io, net32_t x); +extern int unmarshal_net32(IOStream *io, net32_t *x); + +extern int marshal_string(IOStream *io, char *s, uint32_t s_n); +extern int unmarshal_string(IOStream *io, char *s, uint32_t s_n); +extern int unmarshal_new_string(IOStream *io, char **s, uint32_t *s_n); + +#endif /* ! _SP_MARSHAL_H_ */ diff --git a/tools/lib/socket_stream.c b/tools/lib/socket_stream.c new file mode 100644 index 0000000000..cfa6e3abf0 --- /dev/null +++ b/tools/lib/socket_stream.c @@ -0,0 +1,259 @@ +/* $Id: socket_stream.c,v 1.9 2004/03/05 14:45:34 mjw Exp $ */ +/* + * Copyright (C) 2004 Mike Wray + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/** @file + * An IOStream implementation using sockets. + */ + +#include +#include +#include +#include +#include +#include "allocate.h" +#include "socket_stream.h" + +#define MODULE_NAME "sock" +#define DEBUG 0 +//#undef DEBUG +#include "debug.h" + +static int socket_read(IOStream *s, void *buf, size_t n); +static int socket_write(IOStream *s, const void *buf, size_t n); +static int socket_error(IOStream *s); +static int socket_close(IOStream *s); +static void socket_free(IOStream *s); +static int socket_flush(IOStream *s); + +/** Methods used by a socket IOStream. */ +static const IOMethods socket_methods = { + read: socket_read, + write: socket_write, + error: socket_error, + close: socket_close, + free: socket_free, + flush: socket_flush, +}; + +/** Get the socket data. + * + * @param io socket stream + * @return data + */ +static inline SocketData * socket_data(IOStream *io){ + return (SocketData *)io->data; +} + +/** Test if a stream is a socket stream. + * + * @param io stream + * @return 0 if a socket stream, -EINVAL if not + */ +int socket_stream_check(IOStream *io){ + return (io && io->methods == &socket_methods ? 0 : -EINVAL); +} + +/** Get the data for a socket stream. + * + * @param io stream + * @param data return value for the data + * @return 0 if a socket stream, -EINVAL if not + */ +int socket_stream_data(IOStream *io, SocketData **data){ + int err = socket_stream_check(io); + if(err){ + *data = NULL; + } else { + *data = socket_data(io); + } + return err; +} + +/** Set the destination address for a socket stream. + * + * @param io stream + * @param addr address + * @return 0 if a socket stream, -EINVAL if not + */ +int socket_stream_set_addr(IOStream *io, struct sockaddr_in *addr){ + int err = 0; + SocketData *data = NULL; + err = socket_stream_data(io, &data); + if(!err){ + data->daddr = *addr; + } + return err; +} + +/** Set the send flags for a socket stream. + * + * @param io stream + * @param flags flags + * @return 0 if a socket stream, -EINVAL if not + */ +int socket_stream_set_flags(IOStream *io, int flags){ + int err = 0; + SocketData *data = NULL; + err = socket_stream_data(io, &data); + if(!err){ + data->flags = flags; + } + return err; +} + +/** Write to the underlying socket using sendto. + * + * @param stream input + * @param buf where to put input + * @param n number of bytes to write + * @return number of bytes written + */ +static int socket_write(IOStream *s, const void *buf, size_t n){ + SocketData *data = socket_data(s); + struct sockaddr *daddr = (struct sockaddr *)&data->daddr; + socklen_t daddr_n = sizeof(data->daddr); + int k; + dprintf("> sock=%d addr=%s:%d n=%d\n", + data->fd, inet_ntoa(data->daddr.sin_addr), ntohs(data->daddr.sin_port), n); + if(0){ + struct sockaddr_in self = {}; + socklen_t self_n; + getsockname(data->fd, (struct sockaddr *)&self, &self_n); + dprintf("> sockname sock=%d %s:%d\n", + data->fd, inet_ntoa(self.sin_addr), ntohs(self.sin_port)); + } + k = sendto(data->fd, buf, n, data->flags, daddr, daddr_n); + dprintf("> sendto=%d\n", k); + return k; +} + +/** Read from the underlying stream using recv(); + * + * @param stream input + * @param buf where to put input + * @param n number of bytes to read + * @return number of bytes read + */ +static int socket_read(IOStream *s, void *buf, size_t n){ + SocketData *data = socket_data(s); + int k; + struct sockaddr *saddr = (struct sockaddr *)&data->saddr; + socklen_t saddr_n = sizeof(data->saddr); + k = recvfrom(data->fd, buf, n, data->flags, saddr, &saddr_n); + return k; +} + +/** Print to the underlying socket. + * + * @param s socket stream + * @param msg format to use + * @param args arguments + * @return result of the print + */ +static int socket_print(IOStream *s, const char *msg, va_list args){ + SocketData *data = socket_data(s); + int n; + n = vsnprintf(data->buf, data->buf_n - 1, msg, args); + if(0 < n && n < data->buf_n){ + socket_write(s, data->buf, n); + } + return n; +} + +/** Read a character from the underlying socket + * + * @param s socket stream + * @return character read, IOSTREAM_EOF on end of socket (or error) + */ +static int socket_getc(IOStream *s){ + char b; + int n, c; + n = socket_read(s, &b, 1); + c = (n <= 0 ? IOSTREAM_EOF : b); + return c; +} + +/** Flush the socket (no-op). + * + * @param s socket stream + * @return 0 on success, error code otherwise + */ +static int socket_flush(IOStream *s){ + return 0; +} + +/** Check if a socket stream has an error (no-op). + * + * @param s socket stream + * @return 1 if has an error, 0 otherwise + */ +static int socket_error(IOStream *s){ + // Read SOL_SOCKET/SO_ERROR ? + return 0; +} + +/** Close a socket stream. + * + * @param s socket stream to close + * @return result of the close + */ +static int socket_close(IOStream *s){ + SocketData *data = socket_data(s); + return close(data->fd); +} + +/** Free a socket stream. + * + * @param s socket stream + */ +static void socket_free(IOStream *s){ + SocketData *data = socket_data(s); + deallocate(data); +} + +/** Create an IOStream for a socket. + * + * @param fd socket to wtap + * @return new IOStream using fd for i/o + */ +IOStream *socket_stream_new(int fd){ + int err = -ENOMEM; + IOStream *io = NULL; + SocketData *data = NULL; + + io = ALLOCATE(IOStream); + if(!io) goto exit; + io->methods = &socket_methods; + data = ALLOCATE(SocketData); + if(!data) goto exit; + io->data = data; + data->fd = fd; + data->buf_n = sizeof(data->buf); + err = 0; + exit: + if(err){ + if(io){ + if(data) deallocate(data); + deallocate(io); + io = NULL; + } + } + return io; +} + diff --git a/tools/lib/socket_stream.h b/tools/lib/socket_stream.h new file mode 100644 index 0000000000..5b8515f6b3 --- /dev/null +++ b/tools/lib/socket_stream.h @@ -0,0 +1,54 @@ +/* $Id: socket_stream.h,v 1.2 2004/03/04 17:38:13 mjw Exp $ */ +/* + * Copyright (C) 2004 Mike Wray + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _XEN_LIB_SOCKET_STREAM_H_ +#define _XEN_LIB_SOCKET_STREAM_H_ + +#ifndef __KERNEL__ +#include "iostream.h" +#include + +#include +#include +#include + +/** Data associated with a socket stream. */ +typedef struct SocketData { + /** The socket file descriptor. */ + int fd; + /** Source address from last read (recvfrom). */ + struct sockaddr_in saddr; + /** Destination address for writes (sendto). */ + struct sockaddr_in daddr; + /** Write flags (sendto). */ + int flags; + /** Buffer size. */ + int buf_n; + /** Buffer for formatted printing. */ + char buf[1024]; +} SocketData; + +extern IOStream *socket_stream_new(int fd); +extern int socket_stream_data(IOStream *io, SocketData **data); +extern int socket_stream_check(IOStream *io); +extern int socket_stream_set_addr(IOStream *io, struct sockaddr_in *addr); +extern int socket_stream_set_flags(IOStream *io, int flags); + +#endif +#endif /* !_XEN_LIB_SOCKET_STREAM_H_ */ diff --git a/tools/lib/string_stream.c b/tools/lib/string_stream.c new file mode 100644 index 0000000000..c3cf423d84 --- /dev/null +++ b/tools/lib/string_stream.c @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2001, 2002 Hewlett-Packard Company. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/** @file + * IOStream subtype for input and output to strings. + * Usable from user or kernel code (with __KERNEL__ defined). + */ + +#include "sys_string.h" +#include "string_stream.h" +#include "allocate.h" + +static int string_print(IOStream *io, const char *msg, va_list args); +static int string_getc(IOStream *io); +static int string_error(IOStream *io); +static int string_close(IOStream *io); +static void string_free(IOStream *io); + +/** Methods for a string stream. */ +static IOMethods string_methods = { + //print: string_print, + //getc: string_getc, + error: string_error, + close: string_close, + free: string_free, +}; + +/** Get the string stream state. + * + * @param io string stream + * @return state + */ +static inline StringData *get_string_data(IOStream *io){ + return (StringData*)io->data; +} + +/** Read a character from a string stream. + * + * @param io string stream + * @return character read, IOSTREAM_EOF if no more input + */ +static int string_getc(IOStream *io){ + StringData *data = get_string_data(io); + int c = IOSTREAM_EOF; + char *s = data->in; + + if(s && s < data->end){ + c = (unsigned)*s; + data->in = s+1; + } + return c; +} + +/** Print to a string stream. + * Formats the data to an internal buffer and prints it. + * The formatted data must fit into the internal buffer. + * + * @param io string stream + * @param format print format + * @param args print arguments + * @return result of the print + */ +static int string_print(IOStream *io, const char *msg, va_list args){ + StringData *data = get_string_data(io); + int k = data->end - data->out; + int n = vsnprintf(data->out, k, (char*)msg, args); + if(n < 0 || n > k ){ + n = k; + IOStream_close(io); + } else { + data->out += n; + } + return n; +} + +/** Test if a string stream has an error. + * + * @param io string stream + * @return 0 if ok, error code otherwise + */ +static int string_error(IOStream *io){ + StringData *data = get_string_data(io); + return data->out == NULL; +} + +/** Close a string stream. + * + * @param io string stream + * @return 0 + */ +static int string_close(IOStream *io){ + StringData *data = get_string_data(io); + data->in = NULL; + data->out = NULL; + return 0; +} + +/** Free a string stream. + * The stream must have been allocated, not statically created. + * The stream state is freed, but the underlying string is not. + * + * @param io string stream + */ +static void string_free(IOStream *io){ + StringData *data = get_string_data(io); + zero(data, sizeof(*data)); + deallocate(data); +} + +/** Get the methods to use for a string stream. + * + * @return methods + */ +IOMethods *string_stream_get_methods(void){ + return &string_methods; +} + +/** Initialise a string stream, usually from static data. + * + * @param io address of IOStream to fill in + * @param data address of StringData to fill in + * @param s string to use + * @param n length of the string + */ +void string_stream_init(IOStream *io, StringData *data, char *s, int n){ + if(data && io){ + zero(data, sizeof(*data)); + data->string = (char*)s; + data->in = data->string; + data->out = data->string; + data->size = n; + data->end = data->string + n; + zero(io, sizeof(*io)); + io->methods = &string_methods; + io->data = data; + } +} + +/** Allocate and initialise a string stream. + * + * @param s string to use + * @param n length of the string + * @return new stream (free using IOStream_free) + */ +IOStream *string_stream_new(char *s, int n){ + int ok = 0; + StringData *data = ALLOCATE(StringData); + IOStream *io = ALLOCATE(IOStream); + if(data && io){ + ok = 1; + string_stream_init(io, data, s, n); + } + if(!ok){ + deallocate(data); + deallocate(io); + io = NULL; + } + return io; +} diff --git a/tools/lib/string_stream.h b/tools/lib/string_stream.h new file mode 100644 index 0000000000..36d764b265 --- /dev/null +++ b/tools/lib/string_stream.h @@ -0,0 +1,46 @@ +/* $Id: string_stream.h,v 1.1 2003/08/22 14:25:48 mjw Exp $ */ +/* + * Copyright (C) 2001, 2002 Hewlett-Packard Company. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _SP_STRING_STREAM_H_ +#define _SP_STRING_STREAM_H_ + +#include "iostream.h" + +/** Internal state for a string stream. + * Exposed here so that string streams can be statically created, using + * string_stream_init(). + */ +typedef struct { + /** The string used for input and ouput. */ + char *string; + /** Output pointer. */ + char *out; + /** Input pointer. */ + char *in; + /** Length of string. */ + int size; + /** End marker. */ + char *end; +} StringData; + +extern IOMethods *string_stream_get_methods(void); +extern IOStream *string_stream_new(char *s, int n); +extern void string_stream_init(IOStream *stream, StringData *data, char *s, int n); + +#endif /* !_SP_STRING_STREAM_H_ */ diff --git a/tools/lib/sxpr.c b/tools/lib/sxpr.c new file mode 100644 index 0000000000..adeffbe5eb --- /dev/null +++ b/tools/lib/sxpr.c @@ -0,0 +1,935 @@ +/* + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. This library is + * distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include "sys_string.h" +#include "lexis.h" +#include "sys_net.h" +#include "hash_table.h" +#include "sxpr.h" + +#include +#undef free + +/** @file + * General representation of sxprs. + * Includes print, equal, and free functions for the sxpr types. + * + * Zero memory containing an Sxpr will have the value ONONE - this is intentional. + * When a function returning an sxpr cannot allocate memory we return ONOMEM. + * + */ + +static int atom_print(IOStream *io, Sxpr obj, unsigned flags); +static int atom_equal(Sxpr x, Sxpr y); +static void atom_free(Sxpr obj); + +static int string_print(IOStream *io, Sxpr obj, unsigned flags); +static int string_equal(Sxpr x, Sxpr y); +static void string_free(Sxpr obj); + +static int cons_print(IOStream *io, Sxpr obj, unsigned flags); +static int cons_equal(Sxpr x, Sxpr y); +static void cons_free(Sxpr obj); + +static int null_print(IOStream *io, Sxpr obj, unsigned flags); +static int none_print(IOStream *io, Sxpr obj, unsigned flags); +static int int_print(IOStream *io, Sxpr obj, unsigned flags); +static int bool_print(IOStream *io, Sxpr obj, unsigned flags); + +/** Type definitions. */ +static SxprType types[1024] = { + [T_NONE] { type: T_NONE, name: "none", print: none_print }, + [T_NULL] { type: T_NULL, name: "null", print: null_print }, + [T_UINT] { type: T_UINT, name: "int", print: int_print, }, + [T_BOOL] { type: T_BOOL, name: "bool", print: bool_print, }, + [T_ATOM] { type: T_ATOM, name: "atom", print: atom_print, + pointer: TRUE, + free: atom_free, + equal: atom_equal, + }, + [T_STRING] { type: T_STRING, name: "string", print: string_print, + pointer: TRUE, + free: string_free, + equal: string_equal, + }, + [T_CONS] { type: T_CONS, name: "cons", print: cons_print, + pointer: TRUE, + free: cons_free, + equal: cons_equal, + }, +}; + +/** Number of entries in the types array. */ +static int type_sup = sizeof(types)/sizeof(types[0]); + +/** Get the type definition for a given type code. + * + * @param ty type code + * @return type definition or null + */ +SxprType *get_sxpr_type(int ty){ + if(0 <= ty && ty < type_sup){ + return types+ty; + } + return NULL; +} + +/** The default print function. + * + * @param io stream to print to + * @param x sxpr to print + * @param flags print flags + * @return number of bytes written on success + */ +int default_print(IOStream *io, Sxpr x, unsigned flags){ + return IOStream_print(io, "#<%u %lu>\n", get_type(x), get_ul(x)); +} + +/** The default equal function. + * Uses eq(). + * + * @param x sxpr to compare + * @param y sxpr to compare + * @return 1 if equal, 0 otherwise + */ +int default_equal(Sxpr x, Sxpr y){ + return eq(x, y); +} + +/** General sxpr print function. + * Prints an sxpr on a stream using the print function for the sxpr type. + * Printing is controlled by flags from the PrintFlags enum. + * If PRINT_TYPE is in the flags the sxpr type is printed before the sxpr + * (for debugging). + * + * @param io stream to print to + * @param x sxpr to print + * @param flags print flags + * @return number of bytes written + */ +int objprint(IOStream *io, Sxpr x, unsigned flags){ + SxprType *def = get_sxpr_type(get_type(x)); + ObjPrintFn *print_fn = (def && def->print ? def->print : default_print); + int k = 0; + if(!io) return k; + if(flags & PRINT_TYPE){ + k += IOStream_print(io, "%s:", def->name); + } + k += print_fn(io, x, flags); + return k; +} + +/** General sxpr free function. + * Frees an sxpr using the free function for its type. + * Free functions must recursively free any subsxprs. + * If no function is defined then the default is to + * free sxprs whose type has pointer true. + * Sxprs must not be used after freeing. + * + * @param x sxpr to free + */ +void objfree(Sxpr x){ + SxprType *def = get_sxpr_type(get_type(x)); + + if(def){ + if(def->free){ + def->free(x); + } else if (def->pointer){ + hfree(x); + } + } +} + +/** General sxpr equality function. + * Compares x and y using the equal function for x. + * Uses default_equal() if x has no equal function. + * + * @param x sxpr to compare + * @param y sxpr to compare + * @return 1 if equal, 0 otherwise + */ +int objequal(Sxpr x, Sxpr y){ + SxprType *def = get_sxpr_type(get_type(x)); + ObjEqualFn *equal_fn = (def && def->equal ? def->equal : default_equal); + return equal_fn(x, y); +} + +/** Search for a key in an alist. + * An alist is a list of conses, where the cars + * of the conses are the keys. Compares keys using equality. + * + * @param k key + * @param l alist to search + * @return first element of l with car k, or ONULL + */ +Sxpr assoc(Sxpr k, Sxpr l){ + for( ; CONSP(l) ; l = CDR(l)){ + Sxpr x = CAR(l); + if(CONSP(x) && objequal(k, CAR(x))){ + return x; + } + } + return ONULL; +} + +/** Search for a key in an alist. + * An alist is a list of conses, where the cars + * of the conses are the keys. Compares keys using eq. + * + * @param k key + * @param l alist to search + * @return first element of l with car k, or ONULL + */ +Sxpr assocq(Sxpr k, Sxpr l){ + for( ; CONSP(l); l = CDR(l)){ + Sxpr x = CAR(l); + if(CONSP(x) && eq(k, CAR(x))){ + return x; + } + } + return ONULL; +} + +/** Add a new key and value to an alist. + * + * @param k key + * @param l value + * @param l alist + * @return l with the new cell added to the front + */ +Sxpr acons(Sxpr k, Sxpr v, Sxpr l){ + Sxpr x, y; + x = cons_new(k, v); + if(NOMEMP(x)) return x; + y = cons_new(x, l); + if(NOMEMP(y)) cons_free_cells(x); + return y; +} + +/** Test if a list contains an element. + * Uses sxpr equality. + * + * @param l list + * @param x element to look for + * @return a tail of l with x as car, or ONULL + */ +Sxpr cons_member(Sxpr l, Sxpr x){ + for( ; CONSP(l) && !eq(x, CAR(l)); l = CDR(l)){} + return l; +} + +/** Test if a list contains an element satisfying a test. + * The test function is called with v and an element of the list. + * + * @param l list + * @param test_fn test function to use + * @param v value for first argument to the test + * @return a tail of l with car satisfying the test, or 0 + */ +Sxpr cons_member_if(Sxpr l, ObjEqualFn *test_fn, Sxpr v){ + for( ; CONSP(l) && !test_fn(v, CAR(l)); l = CDR(l)){ } + return l; +} + +/** Test if the elements of list 't' are a subset of the elements + * of list 's'. Element order is not significant. + * + * @param s element list to check subset of + * @param t element list to check if is a subset + * @return 1 if is a subset, 0 otherwise + */ +int cons_subset(Sxpr s, Sxpr t){ + for( ; CONSP(t); t = CDR(t)){ + if(!CONSP(cons_member(s, CAR(t)))){ + return 0; + } + } + return 1; +} + +/** Test if two lists have equal sets of elements. + * Element order is not significant. + * + * @param s list to check + * @param t list to check + * @return 1 if equal, 0 otherwise + */ +int cons_set_equal(Sxpr s, Sxpr t){ + return cons_subset(s, t) && cons_subset(t, s); +} + +#ifdef USE_GC +/*============================================================================*/ +/* The functions inside this ifdef are only safe if GC is used. + * Otherwise they may leak memory. + */ + +/** Remove an element from a list (GC only). + * Uses sxpr equality and removes all instances, even + * if there are more than one. + * + * @param l list to remove elements from + * @param x element to remove + * @return modified input list + */ +Sxpr cons_remove(Sxpr l, Sxpr x){ + return cons_remove_if(l, eq, x); +} + +/** Remove elements satisfying a test (GC only). + * The test function is called with v and an element of the set. + * + * @param l list to remove elements from + * @param test_fn function to use to decide if an element should be removed + * @return modified input list + */ +Sxpr cons_remove_if(Sxpr l, ObjEqualFn *test_fn, Sxpr v){ + Sxpr prev = ONULL, elt, next; + + for(elt = l; CONSP(elt); elt = next){ + next = CDR(elt); + if(test_fn(v, CAR(elt))){ + if(NULLP(prev)){ + l = next; + } else { + CDR(prev) = next; + } + } + } + return l; +} + +/** Set the value for a key in an alist (GC only). + * If the key is present, changes the value, otherwise + * adds a new cell. + * + * @param k key + * @param v value + * @param l alist + * @return modified or extended list + */ +Sxpr setf(Sxpr k, Sxpr v, Sxpr l){ + Sxpr e = assoc(k, l); + if(NULLP(e)){ + l = acons(k, v, l); + } else { + CAR(CDR(e)) = v; + } + return l; +} +/*============================================================================*/ +#endif /* USE_GC */ + +/** Create a new atom with the given name. + * + * @param name the name + * @return new atom + */ +Sxpr atom_new(char *name){ + Sxpr n, obj = ONOMEM; + + n = string_new(name); + if(NOMEMP(n)) goto exit; + obj = HALLOC(ObjAtom, T_ATOM); + if(NOMEMP(obj)) goto exit; + OBJ_ATOM(obj)->name = n; + exit: + return obj; +} + +/** Free an atom. + * + * @param obj to free + */ +void atom_free(Sxpr obj){ + // Interned atoms are shared, so do not free. + if(OBJ_ATOM(obj)->interned) return; + objfree(OBJ_ATOM(obj)->name); + hfree(obj); +} + +/** Print an atom. Prints the atom name. + * + * @param io stream to print to + * @param obj to print + * @param flags print flags + * @return number of bytes printed + */ +int atom_print(IOStream *io, Sxpr obj, unsigned flags){ + //return string_print(io, OBJ_ATOM(obj)->name, (flags | PRINT_RAW)); + return string_print(io, OBJ_ATOM(obj)->name, flags); +} + +/** Atom equality. + * + * @param x to compare + * @param y to compare + * @return 1 if equal, 0 otherwise + */ +int atom_equal(Sxpr x, Sxpr y){ + int ok; + ok = eq(x, y); + if(ok) goto exit; + ok = ATOMP(y) && string_equal(OBJ_ATOM(x)->name, OBJ_ATOM(y)->name); + if(ok) goto exit; + ok = STRINGP(y) && string_equal(OBJ_ATOM(x)->name, y); + exit: + return ok; +} + +/** Get the name of an atom. + * + * @param obj atom + * @return name + */ +char * atom_name(Sxpr obj){ + return string_string(OBJ_ATOM(obj)->name); +} + +/** Get the C string from a string sxpr. + * + * @param obj string sxpr + * @return string + */ +char * string_string(Sxpr obj){ + return OBJ_STRING(obj); +} + +/** Get the length of a string. + * + * @param obj string + * @return length + */ +int string_length(Sxpr obj){ + return strlen(OBJ_STRING(obj)); +} + +/** Create a new string. The input string is copied, + * and must be null-terminated. + * + * @param s characters to put in the string + * @return new sxpr + */ +Sxpr string_new(char *s){ + int n = (s ? strlen(s) : 0); + Sxpr obj; + obj = halloc(n+1, T_STRING); + if(!NOMEMP(obj)){ + char *str = OBJ_STRING(obj); + strncpy(str, s, n); + str[n] = '\0'; + } + return obj; +} + +/** Free a string. + * + * @param obj to free + */ +void string_free(Sxpr obj){ + hfree(obj); +} + +/** Determine if a string needs escapes when printed + * using the given flags. + * + * @param str string to check + * @param flags print flags + * @return 1 if needs escapes, 0 otherwise + */ +int needs_escapes(char *str, unsigned flags){ + char *c; + int val = 0; + + if(str){ + for(c=str; *c; c++){ + if(in_alpha_class(*c)) continue; + if(in_decimal_digit_class(*c)) continue; + if(in_class(*c, "/._+:@~-")) continue; + val = 1; + break; + } + } + //printf("\n> val=%d str=|%s|\n", val, str); + return val; +} + +/** Print a string to a stream, with escapes if necessary. + * + * @param io stream to print to + * @param str string + * @param flags print flags + * @return number of bytes written + */ +int _string_print(IOStream *io, char *str, unsigned flags){ + int k = 0; + if((flags & PRINT_RAW) || !needs_escapes(str, flags)){ + k += IOStream_print(io, str); + } else { + k += IOStream_print(io, "\""); + if(str){ + char *s; + for(s = str; *s; s++){ + if(*s < ' ' || *s >= 127 ){ + switch(*s){ + case '\a': k += IOStream_print(io, "\\a"); break; + case '\b': k += IOStream_print(io, "\\b"); break; + case '\f': k += IOStream_print(io, "\\f"); break; + case '\n': k += IOStream_print(io, "\\n"); break; + case '\r': k += IOStream_print(io, "\\r"); break; + case '\t': k += IOStream_print(io, "\\t"); break; + case '\v': k += IOStream_print(io, "\\v"); break; + default: + // Octal escape; + k += IOStream_print(io, "\\%o", *s); + break; + } + } else if(*s == c_double_quote || + *s == c_single_quote || + *s == c_escape){ + k += IOStream_print(io, "\\%c", *s); + } else { + k+= IOStream_print(io, "%c", *s); + } + } + } + k += IOStream_print(io, "\""); + } + return k; +} + +/** Print a string to a stream, with escapes if necessary. + * + * @param io stream to print to + * @param obj string + * @param flags print flags + * @return number of bytes written + */ +int string_print(IOStream *io, Sxpr obj, unsigned flags){ + return _string_print(io, OBJ_STRING(obj), flags); +} + +/** Compare an sxpr with a string for equality. + * + * @param x string to compare with + * @param y sxpr to compare + * @return 1 if equal, 0 otherwise + */ +int string_equal(Sxpr x, Sxpr y){ + int ok = 0; + ok = eq(x,y); + if(ok) goto exit; + ok = has_type(y, T_STRING) && !strcmp(OBJ_STRING(x), OBJ_STRING(y)); + if(ok) goto exit; + ok = has_type(y, T_ATOM) && !strcmp(OBJ_STRING(x), atom_name(y)); + exit: + return ok; +} + +/** Create a new cons cell. + * The cell is ONOMEM if either argument is. + * + * @param car sxpr for the car + * @param cdr sxpr for the cdr + * @return new cons + */ +Sxpr cons_new(Sxpr car, Sxpr cdr){ + Sxpr obj; + if(NOMEMP(car) || NOMEMP(cdr)){ + obj = ONOMEM; + } else { + obj = HALLOC(ObjCons, T_CONS); + if(!NOMEMP(obj)){ + ObjCons *z = OBJ_CONS(obj); + z->car = car; + z->cdr = cdr; + } + } + return obj; +} + +/** Push a new element onto a list. + * + * @param list list to add to + * @param elt element to add + * @return 0 if successful, error code otherwise + */ +int cons_push(Sxpr *list, Sxpr elt){ + Sxpr l; + l = cons_new(elt, *list); + if(NOMEMP(l)) return -ENOMEM; + *list = l; + return 0; +} + +/** Free a cons. Recursively frees the car and cdr. + * + * @param obj to free + */ +void cons_free(Sxpr obj){ + Sxpr next; + for(; CONSP(obj); obj = next){ + next = CDR(obj); + objfree(CAR(obj)); + hfree(obj); + } + if(!NULLP(obj)){ + objfree(obj); + } +} + +/** Free a cons and its cdr cells, but not the car sxprs. + * Does nothing if called on something that is not a cons. + * + * @param obj to free + */ +void cons_free_cells(Sxpr obj){ + Sxpr next; + for(; CONSP(obj); obj = next){ + next = CDR(obj); + hfree(obj); + } +} + +/** Print a cons. + * Prints the cons in list format if the cdrs are conses. + * uses pair (dot) format if the last cdr is not a cons (or null). + * + * @param io stream to print to + * @param obj to print + * @param flags print flags + * @return number of bytes written + */ +int cons_print(IOStream *io, Sxpr obj, unsigned flags){ + int first = 1; + int k = 0; + k += IOStream_print(io, "("); + for( ; CONSP(obj) ; obj = CDR(obj)){ + if(first){ + first = 0; + } else { + k += IOStream_print(io, " "); + } + k += objprint(io, CAR(obj), flags); + } + if(!NULLP(obj)){ + k += IOStream_print(io, " . "); + k += objprint(io, obj, flags); + } + k += IOStream_print(io, ")"); + return (IOStream_error(io) ? -1 : k); +} + +/** Compare a cons with another sxpr for equality. + * If y is a cons, compares the cars and cdrs recursively. + * + * @param x cons to compare + * @param y sxpr to compare + * @return 1 if equal, 0 otherwise + */ +int cons_equal(Sxpr x, Sxpr y){ + return CONSP(y) && + objequal(CAR(x), CAR(y)) && + objequal(CDR(x), CDR(y)); +} + +/** Return the length of a cons list. + * + * @param obj list + * @return length + */ +int cons_length(Sxpr obj){ + int count = 0; + for( ; CONSP(obj); obj = CDR(obj)){ + count++; + } + return count; +} + +/** Destructively reverse a cons list in-place. + * If the argument is not a cons it is returned unchanged. + * + * @param l to reverse + * @return reversed list + */ +Sxpr nrev(Sxpr l){ + if(CONSP(l)){ + // Iterate down the cells in the list making the cdr of + // each cell point to the previous cell. The last cell + // is the head of the reversed list. + Sxpr prev = ONULL; + Sxpr cell = l; + Sxpr next; + + while(1){ + next = CDR(cell); + CDR(cell) = prev; + if(!CONSP(next)) break; + prev = cell; + cell = next; + } + l = cell; + } + return l; +} + +/** Print the null sxpr. + * + * @param io stream to print to + * @param obj to print + * @param flags print flags + * @return number of bytes written + */ +static int null_print(IOStream *io, Sxpr obj, unsigned flags){ + return IOStream_print(io, "()"); +} + +/** Print the `unspecified' sxpr none. + * + * @param io stream to print to + * @param obj to print + * @param flags print flags + * @return number of bytes written + */ +static int none_print(IOStream *io, Sxpr obj, unsigned flags){ + return IOStream_print(io, ""); +} + +/** Print an integer. + * + * @param io stream to print to + * @param obj to print + * @param flags print flags + * @return number of bytes written + */ +static int int_print(IOStream *io, Sxpr obj, unsigned flags){ + return IOStream_print(io, "%d", OBJ_INT(obj)); +} + +/** Print a boolean. + * + * @param io stream to print to + * @param obj to print + * @param flags print flags + * @return number of bytes written + */ +static int bool_print(IOStream *io, Sxpr obj, unsigned flags){ + return IOStream_print(io, (OBJ_UINT(obj) ? k_true : k_false)); +} + +int sxprp(Sxpr obj, Sxpr name){ + return CONSP(obj) && objequal(CAR(obj), name); +} + +/** Get the name of an element. + * + * @param obj element + * @return name + */ +Sxpr sxpr_name(Sxpr obj){ + Sxpr val = ONONE; + if(CONSP(obj)){ + val = CAR(obj); + } else if(STRINGP(obj) || ATOMP(obj)){ + val = obj; + } + return val; +} + +int sxpr_is(Sxpr obj, char *s){ + if(ATOMP(obj)) return !strcmp(atom_name(obj), s); + if(STRINGP(obj)) return !strcmp(string_string(obj), s); + return 0; +} + +int sxpr_elementp(Sxpr obj, Sxpr name){ + return CONSP(obj) && objequal(CAR(obj), name); +} + +/** Get the attributes of an sxpr. + * + * @param obj sxpr + * @return attributes + */ +Sxpr sxpr_attributes(Sxpr obj){ + Sxpr val = ONULL; + if(CONSP(obj)){ + obj = CDR(obj); + if(CONSP(obj)){ + obj = CAR(obj); + if(sxprp(obj, intern("@"))){ + val = CDR(obj); + } + } + } + return val; +} + +Sxpr sxpr_attribute(Sxpr obj, Sxpr key, Sxpr def){ + Sxpr val = ONONE; + val = assoc(sxpr_attributes(obj), key); + if(CONSP(val) && CONSP(CDR(val))){ + val = CADR(def); + } else { + val = def; + } + return val; +} + +/** Get the children of an sxpr. + * + * @param obj sxpr + * @return children + */ +Sxpr sxpr_children(Sxpr obj){ + Sxpr val = ONULL; + if(CONSP(obj)){ + val = CDR(obj); + if(CONSP(val) && sxprp(CAR(val), intern("@"))){ + val = CDR(val); + } + } + return val; +} + +Sxpr sxpr_child(Sxpr obj, Sxpr name, Sxpr def){ + Sxpr val = ONONE; + Sxpr l; + for(l = sxpr_children(obj); CONSP(l); l = CDR(l)){ + if(sxprp(CAR(l), name)){ + val = CAR(l); + break; + } + } + if(NONEP(val)) val = def; + return val; +} + +Sxpr sxpr_child0(Sxpr obj, Sxpr def){ + Sxpr val = ONONE; + Sxpr l = sxpr_children(obj); + if(CONSP(l)){ + val = CAR(l); + } else { + val = def; + } + return val; +} + +Sxpr sxpr_child_value(Sxpr obj, Sxpr name, Sxpr def){ + Sxpr val = ONONE; + val = sxpr_child(obj, name, ONONE); + if(NONEP(val)){ + val = def; + } else { + val = sxpr_child0(val, def); + } + return val; +} + +/** Table of interned symbols. Indexed by symbol name. */ +static HashTable *symbols = NULL; + +/** Hash function for entries in the symbol table. + * + * @param key to hash + * @return hashcode + */ +static Hashcode sym_hash_fn(void *key){ + return hash_string((char*)key); +} + +/** Key equality function for the symbol table. + * + * @param x to compare + * @param y to compare + * @return 1 if equal, 0 otherwise + */ +static int sym_equal_fn(void *x, void *y){ + return !strcmp((char*)x, (char*)y); +} + +/** Entry free function for the symbol table. + * + * @param table the entry is in + * @param entry being freed + */ +static void sym_free_fn(HashTable *table, HTEntry *entry){ + if(entry){ + objfree(((ObjAtom*)entry->value)->name); + HTEntry_free(entry); + } +} + +/** Initialize the symbol table. + * + * @return 0 on sucess, error code otherwise + */ +static int init_symbols(void){ + symbols = HashTable_new(100); + if(symbols){ + symbols->key_hash_fn = sym_hash_fn; + symbols->key_equal_fn = sym_equal_fn; + symbols->entry_free_fn = sym_free_fn; + return 0; + } + return -1; +} + +/** Cleanup the symbol table. Frees the table and all its symbols. + */ +void cleanup_symbols(void){ + HashTable_free(symbols); + symbols = NULL; +} + +/** Get the interned symbol with the given name. + * No new symbol is created. + * + * @return symbol or null + */ +Sxpr get_symbol(char *sym){ + HTEntry *entry; + if(!symbols){ + if(init_symbols()) return ONOMEM; + return ONULL; + } + entry = HashTable_get_entry(symbols, sym); + if(entry){ + return OBJP(T_ATOM, entry->value); + } else { + return ONULL; + } +} + +/** Get the interned symbol with the given name. + * Creates a new symbol if necessary. + * + * @return symbol + */ +Sxpr intern(char *sym){ + Sxpr symbol = get_symbol(sym); + if(NULLP(symbol)){ + if(!symbols) return ONOMEM; + symbol = atom_new(sym); + if(!NOMEMP(symbol)){ + OBJ_ATOM(symbol)->interned = TRUE; + HashTable_add(symbols, atom_name(symbol), get_ptr(symbol)); + } + } + return symbol; +} diff --git a/tools/lib/sxpr.h b/tools/lib/sxpr.h new file mode 100644 index 0000000000..b90083139c --- /dev/null +++ b/tools/lib/sxpr.h @@ -0,0 +1,413 @@ +/* + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. This library is + * distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _XEN_LIB_SXPR_H_ +#define _XEN_LIB_SXPR_H_ + +#include + +#include "hash_table.h" +#include "iostream.h" +#include "allocate.h" + +/** @file + * Definitions for rules and sxprs. + */ + +#ifndef NULL +#define NULL 0 +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +/** Sxpr type. */ +typedef int16_t TypeCode; + +/** A typed sxpr handle.*/ +typedef struct Sxpr { + /** Sxpr type. */ + TypeCode type; + union { + /** Sxpr value. */ + unsigned long ul; + /** Pointer. */ + void *ptr; + } v; +} Sxpr; + +/** Sxpr type to indicate out of memory. */ +#define T_NOMEM ((TypeCode)-1) +/** The 'unspecified' sxpr. */ +#define T_NONE ((TypeCode)0) +/** The empty list. */ +#define T_NULL ((TypeCode)1) +/** Unsigned integer. */ +#define T_UINT ((TypeCode)2) +/** A string. */ +#define T_STRING ((TypeCode)3) +/** An atom. */ +#define T_ATOM ((TypeCode)4) +/** A boolean. */ +#define T_BOOL ((TypeCode)5) + +/** A cons (pair or list). */ +#define T_CONS ((TypeCode)10) + +/** An error. */ +#define T_ERR ((TypeCode)40) + +/** An atom. */ +typedef struct ObjAtom { + Sxpr name; + Hashcode hashcode; + int interned; +} ObjAtom; + +/** A cons (pair). */ +typedef struct ObjCons { + Sxpr car; + Sxpr cdr; +} ObjCons; + +/** A vector. */ +typedef struct ObjVector { + int n; + Sxpr data[0]; +} ObjVector; + +/** Flags for sxpr printing. */ +enum PrintFlags { + PRINT_RAW = 0x001, + PRINT_TYPE = 0x002, + PRINT_PRETTY = 0x004, + PRINT_NUM = 0x008, +}; + +/** An integer sxpr. + * + * @param ty type + * @param val integer value + */ +#define OBJI(ty, val) (Sxpr){ type: (ty), v: { ul: (val) }} + +/** A pointer sxpr. + * If the pointer is non-null, returns an sxpr containing it. + * If the pointer is null, returns ONOMEM. + * + * @param ty type + * @param val pointer + */ +#define OBJP(ty, val) ((val) ? (Sxpr){ type: (ty), v: { ptr: (val) }} : ONOMEM) + +/** Make an integer sxpr containing a pointer. + * + * @param val pointer + */ +#define PTR(val) OBJP(T_UINT, (void*)(val)) + +/** Make an integer sxpr. + * @param x value + */ +#define OINT(x) OBJI(T_UINT, x) + +/** Make an error sxpr. + * + * @param x value + */ +#define OERR(x) OBJI(T_ERR, x) + +/** Out of memory constant. */ +#define ONOMEM OBJI(T_NOMEM, 0) + +/** The `unspecified' constant. */ +#define ONONE OBJI(T_NONE, 0) + +/** Empty list constant. */ +#define ONULL OBJI(T_NULL, 0) + +/** False constant. */ +#define OFALSE OBJI(T_BOOL, 0) + +/** True constant. */ +#define OTRUE OBJI(T_BOOL, 1) + +/* Recognizers for the various sxpr types. */ +#define ATOMP(obj) has_type(obj, T_ATOM) +#define BOOLP(obj) has_type(obj, T_BOOL) +#define CONSP(obj) has_type(obj, T_CONS) +#define ERRP(obj) has_type(obj, T_ERR) +#define INTP(obj) has_type(obj, T_UINT) +#define NOMEMP(obj) has_type(obj, T_NOMEM) +#define NONEP(obj) has_type(obj, T_NONE) +#define NULLP(obj) has_type(obj, T_NULL) +#define STRINGP(obj) has_type(obj, T_STRING) + +#define TRUEP(obj) get_ul(obj) + +/** Convert an sxpr to an unsigned integer. */ +#define OBJ_UINT(x) get_ul(x) +/** Convert an sxpr to an integer. */ +#define OBJ_INT(x) (int)get_ul(x) + +/* Conversions of sxprs to their values. + * No checking is done. + */ +#define OBJ_STRING(x) ((char*)get_ptr(x)) +#define OBJ_CONS(x) ((ObjCons*)get_ptr(x)) +#define OBJ_ATOM(x) ((ObjAtom*)get_ptr(x)) +#define OBJ_SET(x) ((ObjSet*)get_ptr(x)) +#define CAR(x) (OBJ_CONS(x)->car) +#define CDR(x) (OBJ_CONS(x)->cdr) + +#define CAAR(x) (CAR(CAR(x))) +#define CADR(x) (CAR(CDR(x))) +#define CDAR(x) (CDR(CAR(x))) +#define CDDR(x) (CDR(CDR(x))) + +/** Get the integer value from an sxpr. + * + * @param obj sxpr + * @return value + */ +static inline unsigned long get_ul(Sxpr obj){ + return obj.v.ul; +} + +/** Get the pointer value from an sxpr. + * + * @param obj sxpr + * @return value + */ +static inline void * get_ptr(Sxpr obj){ + return obj.v.ptr; +} + +/** Create an sxpr containing a pointer. + * + * @param type typecode + * @param val pointer + * @return sxpr + */ +static inline Sxpr obj_ptr(TypeCode type, void *val){ + return (Sxpr){ type: type, v: { ptr: val } }; +} + +/** Create an sxpr containing an integer. + * + * @param type typecode + * @param val integer + * @return sxpr + */ +static inline Sxpr obj_ul(TypeCode type, unsigned long val){ + return (Sxpr){ type: type, v: { ul: val } }; +} + +/** Get the type of an sxpr. + * + * @param obj sxpr + * @return type + */ +static inline TypeCode get_type(Sxpr obj){ + return obj.type; +} + +/** Check the type of an sxpr. + * + * @param obj sxpr + * @param type to check + * @return 1 if has the type, 0 otherwise + */ +static inline int has_type(Sxpr obj, TypeCode type){ + return get_type(obj) == type; +} + +/** Compare sxprs for literal equality of type and value. + * + * @param x sxpr to compare + * @param y sxpr to compare + * @return 1 if equal, 0 otherwise + */ +static inline int eq(Sxpr x, Sxpr y){ + return ((get_type(x) == get_type(y)) && (get_ul(x) == get_ul(y))); +} + +/** Checked version of CAR + * + * @param x sxpr + * @return CAR if a cons, x otherwise + */ +static inline Sxpr car(Sxpr x){ + return (CONSP(x) ? CAR(x) : x); +} + +/** Checked version of CDR. + * + * @param x sxpr + * @return CDR if a cons, null otherwise + */ +static inline Sxpr cdr(Sxpr x){ + return (CONSP(x) ? CDR(x) : ONULL); +} + +/** Allocate some memory and return an sxpr containing it. + * Returns ONOMEM if allocation failed. + * + * @param n number of bytes to allocate + * @param ty typecode + * @return sxpr + */ +static inline Sxpr halloc(size_t n, TypeCode ty){ + return OBJP(ty, allocate(n)); +} + +/** Allocate an sxpr containing a pointer to the given type. + * + * @param ty type (uses sizeof to determine how many bytes to allocate) + * @param code typecode + * @return sxpr, ONOMEM if allocation failed + */ +#define HALLOC(ty, code) halloc(sizeof(ty), code) + +typedef int ObjPrintFn(IOStream *io, Sxpr obj, unsigned flags); +typedef int ObjEqualFn(Sxpr obj, Sxpr other); +typedef void ObjFreeFn(Sxpr obj); + +/** An sxpr type definition. */ +typedef struct SxprType { + TypeCode type; + char *name; + int pointer; + ObjPrintFn *print; + ObjEqualFn *equal; + ObjFreeFn *free; +} SxprType; + + +extern SxprType *get_sxpr_type(int ty); + +/** Free the pointer in an sxpr. + * + * @param x sxpr containing a pointer + */ +static inline void hfree(Sxpr x){ + deallocate(get_ptr(x)); +} + +extern int objprint(IOStream *io, Sxpr x, unsigned flags); +extern int objequal(Sxpr x, Sxpr y); +extern void objfree(Sxpr x); + +extern void cons_free_cells(Sxpr obj); +extern Sxpr intern(char *s); + +extern Sxpr assoc(Sxpr k, Sxpr l); +extern Sxpr assocq(Sxpr k, Sxpr l); +extern Sxpr acons(Sxpr k, Sxpr v, Sxpr l); +extern Sxpr nrev(Sxpr l); +extern Sxpr cons_member(Sxpr l, Sxpr x); +extern Sxpr cons_member_if(Sxpr l, ObjEqualFn *test_fn, Sxpr v); +extern int cons_subset(Sxpr s, Sxpr t); +extern int cons_set_equal(Sxpr s, Sxpr t); + +#ifdef USE_GC +extern Sxpr cons_remove(Sxpr l, Sxpr x); +extern Sxpr cons_remove_if(Sxpr l, ObjEqualFn *test_fn, Sxpr v); +#endif + +extern Sxpr atom_new(char *name); +extern char * atom_name(Sxpr obj); + +extern Sxpr string_new(char *s); +extern char * string_string(Sxpr obj); +extern int string_length(Sxpr obj); + +extern Sxpr cons_new(Sxpr car, Sxpr cdr); +extern int cons_push(Sxpr *list, Sxpr elt); +extern int cons_length(Sxpr obj); + +Sxpr sxpr_name(Sxpr obj); +int sxpr_is(Sxpr obj, char *s); +int sxpr_elementp(Sxpr obj, Sxpr name); +Sxpr sxpr_attributes(Sxpr obj); +Sxpr sxpr_attribute(Sxpr obj, Sxpr key, Sxpr def); +Sxpr sxpr_children(Sxpr obj); +Sxpr sxpr_child(Sxpr obj, Sxpr name, Sxpr def); +Sxpr sxpr_child0(Sxpr obj, Sxpr def); +Sxpr sxpr_child_value(Sxpr obj, Sxpr name, Sxpr def); + +/** Create a new atom. + * + * @param s atom name + * @return new atom + */ +static inline Sxpr mkatom(char *s){ + return atom_new(s); +} + +/** Create a new string sxpr. + * + * @param s string bytes (copied) + * @return new string + */ +static inline Sxpr mkstring(char *s){ + return string_new(s); +} + +/** Create an integer sxpr. + * + * @param i value + * @return sxpr + */ +static inline Sxpr mkint(int i){ + return OBJI(T_UINT, i); +} + +/** Create a boolean sxpr. + * + * @param b value + * @return sxpr + */ +static inline Sxpr mkbool(int b){ + return OBJI(T_BOOL, (b ? 1 : 0)); +} + +/* Constants used in parsing and printing. */ +#define k_list_open "(" +#define c_list_open '(' +#define k_list_close ")" +#define c_list_close ')' +#define k_true "true" +#define k_false "false" + +#define c_var '$' +#define c_escape '\\' +#define c_single_quote '\'' +#define c_double_quote '"' +#define c_string_open c_double_quote +#define c_string_close c_double_quote +#define c_data_open '[' +#define c_data_close ']' +#define c_binary '*' +#define c_eval '!' +#define c_concat_open '{' +#define c_concat_close '}' + +#endif /* ! _XEN_LIB_SXPR_H_ */ diff --git a/tools/lib/sxpr_parser.c b/tools/lib/sxpr_parser.c new file mode 100644 index 0000000000..16fec56b67 --- /dev/null +++ b/tools/lib/sxpr_parser.c @@ -0,0 +1,897 @@ + +#ifdef __KERNEL__ +# include +# include +# include +# include +# include +#else +# include +# include +#endif + +#include "iostream.h" +#include "lexis.h" +#include "sxpr_parser.h" +#include "sys_string.h" + +/** @file + * Sxpr parsing. + * + * So that the parser does not leak memory, all sxprs constructed by + * the parser must be freed on error. On successful parse the sxpr + * returned becomes the responsibility of the caller. + * + * @author Mike Wray + */ + +#define dprintf(fmt, args...) IOStream_print(iostdout, "[DEBUG] %s" fmt, __FUNCTION__, ##args) +#define printf(fmt, args...) IOStream_print(iostdout, fmt, ##args) + +static void reset(Parser *z); +static int inputchar(Parser *p, char c); +static int savechar(Parser *p, char c); +extern void parse_error(Parser *in); +extern void parse_error_id(Parser *in, ParseErrorId id); + +static int begin_start(Parser *p, char c); +static int state_start(Parser *p, char c); +static int end_start(Parser *p); + +static int begin_comment(Parser *p, char c); +static int state_comment(Parser *p, char c); +static int end_comment(Parser *p); + +static int begin_string(Parser *p, char c); +static int state_string(Parser *p, char c); +static int end_string(Parser *p); +static int state_escape(Parser *p, char c); +static int state_octal(Parser *p, char c); +static int state_hex(Parser *p, char c); + +static int begin_atom(Parser *p, char c); +static int state_atom(Parser *p, char c); +static int end_atom(Parser *p); + +static int state_list(Parser *p, char c); +static int begin_list(Parser *p, char c); +static int end_list(Parser *p); + +/** Print a parse error. + * + * @param in parser + * @param msg format followed by printf arguments + */ +void eprintf(Parser *in, char *msg, ...){ + va_list args; + if(in->error_out){ + va_start(args, msg); + IOStream_vprint(in->error_out, msg, args); + va_end(args); + } +} + +/** Print a parse warning. + * + * @param in parser + * @param msg format followed by printf arguments + */ +void wprintf(Parser *in, char *msg, ...){ + va_list args; + if(in->error_out){ + va_start(args, msg); + IOStream_vprint(in->error_out, msg, args); + va_end(args); + } +} + +/*============================================================================*/ + +/** Record defining the message for a parse error. */ +typedef struct { + ParseErrorId id; + char *message; +} ParseError; + +/** Format for printing parse error messages. */ +#define PARSE_ERR_FMT "parse error> line %3d, column %2d: %s" + +/** Message catalog for the parse error codes. */ +static ParseError catalog[] = { + { PARSE_ERR_UNSPECIFIED, "unspecified error" }, + { PARSE_ERR_NOMEM, "out of memory" }, + { PARSE_ERR_UNEXPECTED_EOF, "unexpected end of input" }, + { PARSE_ERR_TOKEN_TOO_LONG, "token too long" }, + { PARSE_ERR_INVALID_SYNTAX, "syntax error" }, + { PARSE_ERR_INVALID_ESCAPE, "invalid escape" }, + { 0, NULL } +}; + +/** Number of entries in the message catalog. */ +const static int catalog_n = sizeof(catalog)/sizeof(ParseError); + +void ParserState_free(ParserState *z){ + if(!z) return; + objfree(z->val); + deallocate(z); +} + +int ParserState_new(ParserStateFn *fn, ParserState *parent, ParserState **val){ + int err = 0; + ParserState *z; + z = ALLOCATE(ParserState); + if(z){ + z->fn = fn; + z->parent = parent; + z->val = ONULL; + } else { + err = -ENOMEM; + } + if(!err) *val = z; + return err; +} + +/** Free a parser. + * No-op if the parser is null. + * + * @param z parser + */ +void Parser_free(Parser *z){ + if(!z) return; + objfree(z->val); + z->val = ONONE; + deallocate(z); +} + +/** Create a new parser. The error stream defaults to null. + */ +Parser * Parser_new(void){ + Parser *z = ALLOCATE(Parser); + int err = -ENOMEM; + + if(!z) goto exit; + err = 0; + reset(z); + exit: + if(err){ + Parser_free(z); + z = NULL; + } + return z; +} + +/** Get the next character. + * Records the character read in the parser, + * and sets the line and character counts. + * + * @param p parser + * @return error flag: 0 on success, non-zero on error + */ +static int inputchar(Parser *p, char c){ + int err = 0; + if(c=='\n'){ + p->line_no++; + p->char_no = 0; + } else { + p->char_no++; + } + return err; +} + +static int savechar(Parser *p, char c){ + int err = 0; + if(p->buf_i >= p->buf_n){ + err = -ENOMEM; + goto exit; + } + p->buf[p->buf_i] = c; + p->buf_i++; + exit: + return err; +} + +int Parser_input_char(Parser *p, char c){ + int err = 0; + if(at_eof(p)){ + //skip; + } else { + inputchar(p, c); + } + if(!p->state){ + err = begin_start(p, c); + if(err) goto exit; + } + err = p->state->fn(p, c); + exit: + return err; +} + +int Parser_input_eof(Parser *p){ + int err = 0; + p->eof = 1; + err = Parser_input_char(p, IOSTREAM_EOF); + return err; +} + +int Parser_input(Parser *p, char *buf, int buf_n){ + int err = 0; + int i = 0; + if(buf_n <= 0){ + err = Parser_input_eof(p); + goto exit; + } + for(i = 0; istate, &p->state); + return err; +} + +int Parser_pop(Parser *p){ + int err = 0; + ParserState *s = p->state; + p->state = s->parent; + ParserState_free(s); + return err; +} + +int Parser_return(Parser *p){ + int err = 0; + Sxpr val = ONONE; + if(!p->state){ + err = -EINVAL; + goto exit; + } + val = p->state->val; + p->state->val = ONONE; + err = Parser_pop(p); + if(err) goto exit; + if(p->state){ + err = cons_push(&p->state->val, val); + } else { + val = nrev(val); + p->val = val; + } + exit: + if(err){ + objfree(val); + } + return err; +} + +/** Determine if a character is a separator. + * + * @param p parser + * @param c character to test + * @return 1 if a separator, 0 otherwise + */ +static int is_separator(Parser *p, char c){ + return in_sep_class(c); +} + +/** Return the current token. + * The return value points at the internal buffer, so + * it must not be modified (or freed). Use copy_token() if you need a copy. + * + * @param p parser + * @return token + */ +char *peek_token(Parser *p){ + return p->buf; +} + +/** Return a copy of the current token. + * The returned value should be freed when finished with. + * + * @param p parser + * @return copy of token + */ +char *copy_token(Parser *p){ + return strdup(peek_token(p)); +} + +static int do_intern(Parser *p){ + int err = 0; + Sxpr obj = intern(peek_token(p)); + if(NOMEMP(obj)){ + err = -ENOMEM; + } else { + p->state->val = obj; + } + return err; +} + +static int do_string(Parser *p){ + int err = 0; + Sxpr obj; + obj = string_new(peek_token(p)); + if(NOMEMP(obj)){ + err = -ENOMEM; + } else { + p->state->val = obj; + } + return err; +} + +void newtoken(Parser *p){ + memset(p->buf, 0, p->buf_n); + p->buf_i = 0; + p->tok_begin_line = p->line_no; + p->tok_begin_char = p->char_no; +} + +int get_escape(char c, char *d){ + int err = 0; + switch(c){ + case 'a': *d = '\a'; break; + case 'b': *d = '\b'; break; + case 'f': *d = '\f'; break; + case 'n': *d = '\n'; break; + case 'r': *d = '\r'; break; + case 't': *d = '\t'; break; + case 'v': *d = '\v'; break; + case c_escape: *d = c_escape; break; + case c_single_quote: *d = c_single_quote; break; + case c_double_quote: *d = c_double_quote; break; + default: + err = -EINVAL; + } + return err; +} + + +int begin_start(Parser *p, char c){ + return Parser_push(p, state_start); +} + +int state_start(Parser *p, char c){ + int err = 0; + if(at_eof(p)){ + err = end_start(p); + } else if(in_space_class(c)){ + //skip + } else if(in_comment_class(c)){ + begin_comment(p, c); + } else if(c == c_list_open){ + begin_list(p, c); + } else if(c == c_list_close){ + parse_error(p); + err = -EINVAL; + } else if(in_string_quote_class(c)){ + begin_string(p, c); + } else if(in_printable_class(c)){ + begin_atom(p, c); + } else if(c == 0x04){ + //ctrl-D, EOT: end-of-text. + Parser_input_eof(p); + } else { + parse_error(p); + err = -EINVAL; + } + return err; +} + +int end_start(Parser *p){ + int err = 0; + err = Parser_return(p); + return err; +} + +int begin_comment(Parser *p, char c){ + int err = 0; + err = Parser_push(p, state_comment); + if(err) goto exit; + err = inputchar(p, c); + exit: + return err; +} + +int state_comment(Parser *p, char c){ + int err = 0; + if(c == '\n' || at_eof(p)){ + err = end_comment(p); + } else { + err = inputchar(p, c); + } + return err; +} + +int end_comment(Parser *p){ + return Parser_pop(p); +} + +int begin_string(Parser *p, char c){ + int err = 0; + err = Parser_push(p, state_string); + if(err) goto exit; + newtoken(p); + p->state->delim = c; + exit: + return err; +} + +int state_string(Parser *p, char c){ + int err = 0; + if(at_eof(p)){ + parse_error_id(p, PARSE_ERR_UNEXPECTED_EOF); + err = -EINVAL; + } else if(c == p->state->delim){ + err = end_string(p); + } else if(c == '\\'){ + err = Parser_push(p, state_escape); + } else { + err = savechar(p, c); + } + return err; +} + +int end_string(Parser *p){ + int err = 0; + err = do_string(p); + if(err) goto exit; + err = Parser_return(p); + exit: + return err; +} + +int state_escape(Parser *p, char c){ + int err = 0; + char d; + if(at_eof(p)){ + parse_error_id(p, PARSE_ERR_UNEXPECTED_EOF); + err = -EINVAL; + goto exit; + } + if(get_escape(c, &d) == 0){ + err = savechar(p, d); + if(err) goto exit; + err = Parser_pop(p); + } else if(c == 'x'){ + p->state->fn = state_hex; + p->state->ival = 0; + p->state->count = 0; + } else { + p->state->fn = state_octal; + p->state->ival = 0; + p->state->count = 0; + err = Parser_input_char(p, c); + } + exit: + return err; +} + +int octaldone(Parser *p){ + int err = 0; + char d = (char)(p->state->ival & 0xff); + err = Parser_pop(p); + if(err) goto exit; + err = Parser_input_char(p, d); + exit: + return err; +} + +int octaldigit(Parser *p, char c){ + int err = 0; + p->state->ival *= 8; + p->state->ival += c - '0'; + p->state->count++; + if(err) goto exit; + if(p->state->ival < 0 || p->state->ival > 0xff){ + parse_error(p); + err = -EINVAL; + goto exit; + } + if(p->state->count == 3){ + err = octaldone(p); + } + exit: + return err; +} + +int state_octal(Parser *p, char c){ + int err = 0; + if(at_eof(p)){ + parse_error_id(p, PARSE_ERR_UNEXPECTED_EOF); + err = -EINVAL; + goto exit; + } else if('0' <= c && c <= '7'){ + err = octaldigit(p, c); + } else { + err = octaldone(p); + if(err) goto exit; + Parser_input_char(p, c); + } + exit: + return err; +} + +int hexdone(Parser *p){ + int err = 0; + char d = (char)(p->state->ival & 0xff); + err = Parser_pop(p); + if(err) goto exit; + err = Parser_input_char(p, d); + exit: + return err; +} + +int hexdigit(Parser *p, char c, char d){ + int err = 0; + p->state->ival *= 16; + p->state->ival += c - d; + p->state->count++; + if(err) goto exit; + if(p->state->ival < 0 || p->state->ival > 0xff){ + parse_error(p); + err = -EINVAL; + goto exit; + } + if(p->state->count == 2){ + err = hexdone(p); + } + exit: + return err; +} + +int state_hex(Parser *p, char c){ + int err = 0; + if(at_eof(p)){ + parse_error_id(p, PARSE_ERR_UNEXPECTED_EOF); + err = -EINVAL; + goto exit; + } else if('0' <= c && c <= '9'){ + err = hexdigit(p, c, '0'); + } else if('A' <= c && c <= 'F'){ + err = hexdigit(p, c, 'A'); + } else if('a' <= c && c <= 'f'){ + err = hexdigit(p, c, 'a'); + } else if(p->state->count){ + err =hexdone(p); + if(err) goto exit; + Parser_input_char(p, c); + } + exit: + return err; +} + +int begin_atom(Parser *p, char c){ + int err = 0; + err = Parser_push(p, state_atom); + if(err) goto exit; + newtoken(p); + err = savechar(p, c); + exit: + return err; +} + +int state_atom(Parser *p, char c){ + int err = 0; + if(at_eof(p)){ + err = end_atom(p); + } else if(is_separator(p, c) || + in_space_class(c) || + in_comment_class(c)){ + err = end_atom(p); + if(err) goto exit; + err = Parser_input_char(p, c); + } else { + err = savechar(p, c); + } + exit: + return err; +} + +int end_atom(Parser *p){ + int err = 0; + err = do_intern(p); + if(err) goto exit; + err = Parser_return(p); + exit: + return err; +} + +int state_list(Parser *p, char c){ + int err = 0; + if(at_eof(p)){ + parse_error_id(p, PARSE_ERR_UNEXPECTED_EOF); + err = -EINVAL; + } else if(c == c_list_close){ + p->state->val = nrev(p->state->val); + err = end_list(p); + } else { + err = state_start(p, c); + } + return err; + +} + +int begin_list(Parser *p, char c){ + return Parser_push(p, state_list); +} + +int end_list(Parser *p){ + return Parser_return(p); +} + +/** Reset the fields of a parser to initial values. + * + * @param z parser + */ +static void reset(Parser *z){ + IOStream *error_out = z->error_out; + int flags = z->flags; + zero(z, sizeof(Parser)); + z->buf_n = sizeof(z->buf) - 1; + z->buf_i = 0; + z->line_no = 1; + z->char_no = 0; + z->error_out = error_out; + z->flags = flags; +} + +/** Set the parser error stream. + * Parse errors are reported on the the error stream if it is non-null. + * + * @param z parser + * @param error_out error stream + */ +void set_error_stream(Parser *z, IOStream *error_out){ + if(z){ + z->error_out = error_out; + } +} + +/** Get the parser error message for an error code. + * + * @param id error code + * @return error message (empty string if the code is unknown) + */ +static char *get_message(ParseErrorId id){ + int i; + for(i=0; iline_no; +} + +/** Get the column number. + * + * @param in parser + */ +int get_column(Parser *in){ + return in->char_no; +} + +/** Get the line number the current token started on. + * + * @param in parser + */ +int get_tok_line(Parser *in){ + return in->tok_begin_line; +} + +/** Get the column number the current token started on. + * + * @param in parser + */ +int get_tok_column(Parser *in){ + return in->tok_begin_char; +} + +/** Report a parse error. + * Does nothing if the error stream is null or there is no error. + * + * @param in parser + */ +static void report_error(Parser *in){ + if(in->error_out && in->err){ + char *msg = get_message(in->err); + char *tok = peek_token(in); + IOStream_print(in->error_out, PARSE_ERR_FMT, + get_tok_line(in), get_tok_column(in), msg); + if(tok && tok[0]){ + IOStream_print(in->error_out, " '%s'", tok); + } + IOStream_print(in->error_out, "\n"); + } +} + +/** Get the error message for the current parse error code. + * Does nothing if there is no error. + * + * @param in parser + * @param buf where to place the message + * @param n maximum number of characters to place in buf + * @return current error code (zero for no error) + */ +int parse_error_message(Parser *in, char *buf, int n){ + if(in->err){ + char *msg = get_message(in->err); + snprintf(buf, n, PARSE_ERR_FMT, get_tok_line(in), get_tok_column(in), msg); + } + return in->err; +} + +/** Flag an unspecified parse error. All subsequent reads will fail. + * + * @param in parser + */ +void parse_error(Parser *in){ + parse_error_id(in, PARSE_ERR_INVALID_SYNTAX); +} + +/** Flag a parse error. All subsequent reads will fail. + * Does not change the parser error code if it is already set. + * + * @param in parser + * @param id error code + */ +void parse_error_id(Parser *in, ParseErrorId id){ + if(!in->err){ + in->err = id; + report_error(in); + } +} + +/** Test if the parser's error flag is set. + * + * @param in parser + * @return 1 if set, 0 otherwise + */ +int has_error(Parser *in){ + return (in->err > 0); +} + +/** Test if the parser is at end of input. + * + * @param in parser + * @return 1 if at EOF, 0 otherwise + */ +int at_eof(Parser *p){ + return p->eof; +} + +#ifdef SXPR_PARSER_MAIN +/* Stuff for standalone testing. */ + +#include "file_stream.h" +#include "string_stream.h" + +int stringof(Sxpr exp, char **s){ + int err = 0; + if(ATOMP(exp)){ + *s = atom_name(exp); + } else if(STRINGP(exp)){ + *s = string_string(exp); + } else { + err = -EINVAL; + *s = NULL; + } + return err; +} + +int child_string(Sxpr exp, Sxpr key, char **s){ + int err = 0; + Sxpr val = sxpr_child_value(exp, key, ONONE); + err = stringof(val, s); + return err; +} + +int intof(Sxpr exp, int *v){ + int err = 0; + char *s; + unsigned long l; + if(INTP(exp)){ + *v = OBJ_INT(exp); + } else { + err = stringof(exp, &s); + if(err) goto exit; + err = convert_atoul(s, &l); + *v = (int)l; + } + exit: + return err; +} + +int child_int(Sxpr exp, Sxpr key, int *v){ + int err = 0; + Sxpr val = sxpr_child_value(exp, key, ONONE); + err = intof(val, v); + return err; +} + +int eval_vnet(Sxpr exp){ + int err = 0; + Sxpr oid = intern("id"); + int id; + err = child_int(exp, oid, &id); + if(err) goto exit; + dprintf("> vnet id=%d\n", id); + exit: + dprintf("< err=%d\n", err); + return err; +} + +int eval_connect(Sxpr exp){ + int err = 0; + Sxpr ovif = intern("vif"); + Sxpr ovnet = intern("vnet"); + char *vif; + int vnet; + + err = child_string(exp, ovif, &vif); + if(err) goto exit; + err = child_int(exp, ovnet, &vnet); + if(err) goto exit; + dprintf("> connect vif=%s vnet=%d\n", vif, vnet); + exit: + dprintf("< err=%d\n", err); + return err; +} + +int eval(Sxpr exp){ + int err = 0; + Sxpr oconnect = intern("connect"); + Sxpr ovnet = intern("vnet"); + + if(sxpr_elementp(exp, ovnet)){ + err = eval_vnet(exp); + } else if(sxpr_elementp(exp, oconnect)){ + err = eval_connect(exp); + } else { + err = -EINVAL; + } + return err; +} + +/** Main program for testing. + * Parses input and prints it. + * + * @param argc number of arguments + * @param argv arguments + * @return error code + */ +int main(int argc, char *argv[]){ + Parser *pin; + int err = 0; + char buf[1024]; + int k; + Sxpr obj, l, x; + + pin = Parser_new(); + set_error_stream(pin, iostdout); + dprintf("> parse...\n"); + while(1){ + k = fread(buf, 1, 1024, stdin); + err = Parser_input(pin, buf, k); + dprintf("> Parser_input=%d\n", err); + if(k <= 0) break; + } + obj = pin->val; + for(l = obj ; CONSP(l); l = CDR(l)){ + x = CAR(l); + objprint(iostdout, x, 0); printf("\n"); + eval(x); + } + dprintf("> err=%d\n", err); + return 0; +} +#endif diff --git a/tools/lib/sxpr_parser.h b/tools/lib/sxpr_parser.h new file mode 100644 index 0000000000..7296312e44 --- /dev/null +++ b/tools/lib/sxpr_parser.h @@ -0,0 +1,125 @@ +/* + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. This library is + * distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _XEN_LIB_SXPR_PARSER_H_ +#define _XEN_LIB_SXPR_PARSER_H_ + +#include "sxpr.h" +#include "iostream.h" + +/** @file + * Sxpr parsing definitions. + */ + +/** Size of a parser input buffer. + * Tokens read must fit into this size (including trailing null). + */ +#define PARSER_BUF_SIZE 1024 + +struct Parser; +typedef int ParserStateFn(struct Parser *, char c); + +typedef struct ParserState { + struct ParserState *parent; + Sxpr val; + int ival; + int count; + char delim; + ParserStateFn *fn; +} ParserState; + +/** Structure representing an input source for the parser. + * Can read from any IOStream implementation. + */ +typedef struct Parser { + Sxpr val; + /** Error reporting stream (null for no reports). */ + IOStream *error_out; + int eof; + /** Error flag. Non-zero if there has been a read error. */ + int err; + /** Line number on input (from 1). */ + int line_no; + /** Column number of input (reset on new line). */ + int char_no; + /** Lookahead character. */ + char c; + /** Buffer for reading tokens. */ + char buf[PARSER_BUF_SIZE]; + /** Size of token buffer. */ + int buf_n; + int buf_i; + /** Line the last token started on. */ + int tok_begin_line; + /** Character number the last token started on. */ + int tok_begin_char; + /** Parsing flags. */ + int flags; + ParserState *state; +} Parser; + +/** Parser error codes. */ +typedef enum { + PARSE_ERR_NONE=0, + PARSE_ERR_UNSPECIFIED, + PARSE_ERR_NOMEM, + PARSE_ERR_UNEXPECTED_EOF, + PARSE_ERR_TOKEN_TOO_LONG, + PARSE_ERR_INVALID_SYNTAX, + PARSE_ERR_INVALID_ESCAPE, +} ParseErrorId; + + +/** Parser flags. */ +//enum { +//}; + +/** Raise some parser flags. + * + * @param in parser + * @param flags flags mask + */ +inline static void parser_flags_raise(Parser *in, int flags){ + in->flags |= flags; +} + +/** Lower some parser flags. + * + * @param in parser + * @param flags flags mask + */ +inline static void parser_flags_lower(Parser *in, int flags){ + in->flags &= ~flags; +} + +/** Clear all parser flags. + * + * @param in parser + */ +inline static void parser_flags_clear(Parser *in){ + in->flags = 0; +} + +extern void Parser_free(Parser *z); +extern Parser * Parser_new(void); +extern int Parser_input(Parser *p, char *buf, int buf_n); +extern int Parser_input_eof(Parser *p); + +extern int parse_error_message(Parser *in, char *buf, int n); +extern int has_error(Parser *in); +extern int at_eof(Parser *in); + +#endif /* ! _XEN_LIB_SXPR_PARSER_H_ */ diff --git a/tools/lib/sys_ctype.h b/tools/lib/sys_ctype.h new file mode 100644 index 0000000000..1dc6cf2fac --- /dev/null +++ b/tools/lib/sys_ctype.h @@ -0,0 +1,12 @@ +#ifndef _XENO_SYS_CTYPE_H_ +#define _XENO_SYS_CTYPE_H_ +/** @file + ** Replacement for ctype include that can be used + * from user or kernel code. + */ +#ifdef __KERNEL__ +# include +#else +# include +#endif +#endif /* ! _XENO_SYS_CTYPE_H_ */ diff --git a/tools/lib/sys_net.c b/tools/lib/sys_net.c new file mode 100644 index 0000000000..0e7ac5d638 --- /dev/null +++ b/tools/lib/sys_net.c @@ -0,0 +1,309 @@ +/* + * Copyright (C) 2001 - 2004 Mike Wray + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. This library is + * distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "sys_net.h" +#include "sys_string.h" + +/** @file + * All network data are kept in network order and only converted to + * host order for display. Network data includes IP addresses, port numbers and + * network masks. + */ + +/** Maximum value for a port. */ +#define PORT_MAX 0xffff + +/** Convert a number of bits to a network mask + * for IP addresses. The number of bits must + * be in the range 1-31. + * + * @param n number of bits to set in the mask + * @return value with n high bits set (in network order) + */ +unsigned long bits_to_mask(int n){ + unsigned long mask = (n ? (1 << 31) : 0); + int i; + for(i=1; i> 1); + } + return htonl(mask); +} + +/** Convert a network mask to a number of bits. + * + * @param mask network mask in network order + * @return number of bits in mask + */ +int mask_to_bits(unsigned long mask){ + // Start with n set to the number of bits in the mask. Then reduce n by + // the number of low zero bits in the mask. + int n = 32; + for(mask = ntohl(mask); + (mask & 1)==0 && n>0; + mask >>= 1){ + n--; + } + return n; +} + +/** Get the index of the first occurrence of a character in a string. + * Stops at end of string or after n characters. + * + * @param s input string + * @param n maximum number of charactes to search + * @param c character to look for + * @return index of first occurrence, -1 if not found + */ +inline static int indexof(const char *s, int n, char c){ + int i; + for(i=0; i= sizeof(buf)){ + goto exit; + } + for(i=0; i < WORD_BYTES; i++){ + int idx = indexof(s, n, dot); + idx = (idx < 0 ? strlen(s) : idx); + strncpy(buf, s, idx); buf[idx]='\0'; + if(convert_atoul(buf, &v)){ + goto exit; + } + if(v < 0 || v > ADDR_MAX){ + goto exit; + } + addr |= (v << shift); + if(idx == n) break; + shift -= BYTE_BITS; + s += idx+1; + } + err = 0; + exit: + addr = htonl(addr); + *address = (err ? 0 : addr); + return err; +} + +#ifdef __KERNEL__ +/** Convert an address in network order to IPv4 dot notation. + * The return value is a static buffer which is overwritten on each call. + * + * @param inaddr address (in network order) + * @return address in dot notation + */ +char *inet_ntoa(struct in_addr inaddr){ + static char address[16] = {}; + uint32_t addr = ntohl(inaddr.s_addr); + snprintf(address, sizeof(address), "%d.%d.%d.%d", + (unsigned)((addr >> 24) & 0xff), + (unsigned)((addr >> 16) & 0xff), + (unsigned)((addr >> 8) & 0xff), + (unsigned)((addr ) & 0xff)); + return address; +} + + +/** Convert a string in IPv4 dot notation to an int in network order. + * + * @param address address in dot notation + * @param inp result of conversion (in network order) + * @return 0 on success, error code on error + */ +int inet_aton(const char *address, struct in_addr *inp){ + int err = 0; + unsigned long addr; + + err = get_inet_addr(address, &addr); + if(err) goto exit; + inp->s_addr = addr; + exit: + return err; +} +#endif + +/** Convert a hostname or IPv4 address string to an address in network order. + * + * @param name input hostname or address string + * @param address where to put the address + * @return 1 if address found OK, 0 otherwise + */ +int get_host_address(const char *name, unsigned long *address){ +#ifdef __KERNEL__ + return get_inet_addr(name, address) == 0; +#else + struct hostent *host = gethostbyname(name); + if(!host){ + return 0; + } + *address = ((struct in_addr *)(host->h_addr))->s_addr; + return 1; +#endif +} + +/** Convert a service name to a port (in network order). + * + * @param name service name + * @param port where to put the port + * @return 1 if service port found OK, 0 otherwise + */ +int get_service_port(const char *name, unsigned long *port){ +#ifdef __KERNEL__ + return 0; +#else + struct servent *service; + service = getservbyname(name, 0); + if(!service){ + return 0; + } + *port = service->s_port; + return 1; +#endif +} + +/** Convert a port number (in network order) to a service name. + * + * @param port the port number + * @return service name if found OK, 0 otherwise + */ +char *get_port_service(unsigned long port){ +#ifdef __KERNEL__ + return 0; +#else + struct servent *service = getservbyport(port, 0); + return (service ? service->s_name : 0); +#endif +} + +/** Convert a decimal integer or service name to a port (in network order). + * + * @param s input to convert + * @param port where to put the port + * @return 1 if port found OK, 0 otherwise + */ +int convert_service_to_port(const char *s, unsigned long *port){ + int ok = 0; + unsigned long value; + if(convert_atoul(s, &value)){ + ok = get_service_port(s, &value); + } else { + ok = (0 <= value) && (value <= PORT_MAX); + value = htons((unsigned short)value); + } + *port = (ok ? value : 0); + return ok; +} + +#define MAC_ELEMENT_N 6 // Number of elements in a MAC address. +#define MAC_DIGIT_N 2 // Number of digits in an element in a MAC address. +#define MAC_LENGTH 17 //((MAC_ELEMENT_N * MAC_DIGIT_N) + MAC_ELEMENT_N - 1) + +/** Convert a mac address from a string of the form + * XX:XX:XX:XX:XX:XX to numerical form (an array of 6 unsigned chars). + * Each X denotes a hex digit: 0..9, a..f, A..F. + * Also supports using '-' as the separator instead of ':'. + * + * @param mac_in string to convert + * @param mac destination for the value + * @return 0 on success, -1 on error + */ +int mac_aton(const char *mac_in, unsigned char *mac){ + int err = 0; + int i, j; + const char *p; + char sep = 0; + unsigned char d; + if(!mac_in || strlen(mac_in) != MAC_LENGTH){ + err = -1; + goto exit; + } + for(i = 0, p = mac_in; i < MAC_ELEMENT_N; i++){ + d = 0; + if(i){ + if(!sep){ + if(*p == ':' || *p == '-') sep = *p; + } + if(sep && *p == sep){ + p++; + } else { + err = -1; + goto exit; + } + } + for(j = 0; j < MAC_DIGIT_N; j++, p++){ + if(j) d <<= 4; + if(*p >= '0' && *p <= '9'){ + d += (*p - '0'); + } else if(*p >= 'A' && *p <= 'F'){ + d += (*p - 'A') + 10; + } else if(*p >= 'a' && *p <= 'f'){ + d += (*p - 'a') + 10; + } else { + err = -1; + goto exit; + } + } + mac[i] = d; + } + exit: + return err; +} + +/** Convert a MAC address from numerical form to a string. + * + * @param mac address to convert + * @return static string value + */ +char *mac_ntoa(const unsigned char *mac){ + static char buf[MAC_LENGTH + 1]; + int buf_n = sizeof(buf); + + memset(buf, buf_n, 0); + snprintf(buf, buf_n, "%02x:%02x:%02x:%02x:%02x:%02x", + mac[0], mac[1], mac[2], + mac[3], mac[4], mac[5]); + buf[buf_n - 1] = '\0'; + return buf; +} diff --git a/tools/lib/sys_net.h b/tools/lib/sys_net.h new file mode 100644 index 0000000000..da6c1e8fd5 --- /dev/null +++ b/tools/lib/sys_net.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2001 - 2004 Mike Wray + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _XEN_LIB_SYS_NET_H_ +#define _XEN_LIB_SYS_NET_H_ +/** @file + * + * Replacement for standard network includes. + * Works in user or kernel code. + */ + +extern int get_inet_addr(const char *s, unsigned long *address); +extern unsigned long bits_to_mask(int n); +extern int mask_to_bits(unsigned long mask); +extern int get_host_address(const char *name, unsigned long *address); +extern int get_service_port(const char *name, unsigned long *port); +extern char *get_port_service(unsigned long port); +extern int convert_service_to_port(const char *s, unsigned long *port); + +#ifdef __KERNEL__ +#include +#include +#include +#include +#include + +#ifndef htonl +#define htonl(x) __constant_htonl(x) +#endif + +#ifndef ntohl +#define ntohl(x) __constant_ntohl(x) +#endif + +#ifndef htons +#define htons(x) __constant_htons(x) +#endif + +#ifndef ntohs +#define ntohs(x) __constant_ntohs(x) +#endif + +#include +extern char *inet_ntoa(struct in_addr inaddr); +extern int inet_aton(const char *address, struct in_addr *inp); + +#else + +#include +#include +#include +#include +#include + +#endif + +extern char *mac_ntoa(const unsigned char *macaddr); +extern int mac_aton(const char *addr, unsigned char *macaddr); + +#endif /* !_SP_SYS_NET_H_ */ + + + diff --git a/tools/lib/sys_string.c b/tools/lib/sys_string.c new file mode 100644 index 0000000000..13a90dfd7d --- /dev/null +++ b/tools/lib/sys_string.c @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2001 - 2004 Mike Wray + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef __KERNEL__ +# include +# include +# include +# include +#else +# include +#endif + +#include "allocate.h" +#include "sys_string.h" + +/** Set the base to use for converting a string to a number. Base is + * hex if starts with 0x, otherwise decimal. + * + * @param s input string + * @param base where to put the base + * @return rest of s to parse as a number + */ +inline static const char * convert_set_base(const char *s, int *base){ + *base = 10; + if(s){ + if(*s=='0'){ + s++; + if(*s=='x' || *s=='X'){ + *base = 16; + s++; + } + } + } + return s; +} + +/** Get the numerical value of a digit in the given base. + * + * @param c digit character + * @param base to use + * @return numerical value of digit in range 0..base-1 or + * -1 if not in range for the base + */ +inline static int convert_get_digit(char c, int base){ + int d; + + if('0'<=c && c<='9'){ + d = c - '0'; + } else if('a'<=c && c<='f'){ + d = c - 'a' + 10; + } else if('A'<=c && c<='F'){ + d = c - 'A' + 10; + } else { + d = -1; + } + return (d < base ? d : -1); +} + +/** Convert a string to an unsigned long by parsing it as a number. + * Will accept hex or decimal in usual C syntax. + * + * @param str input string + * @param val where to put the result + * @return 0 if converted OK, negative otherwise + */ +int convert_atoul(const char *str, unsigned long *val){ + int err = 0; + unsigned long v = 0; + int base; + const char *s = str; + + if(!s) { + err = -EINVAL; + goto exit; + } + s = convert_set_base(s, &base); + for( ; !err && *s; s++){ + int digit = convert_get_digit(*s, base); + if(digit<0){ + err = -EINVAL; + goto exit; + } + v *= base; + v += digit; + } + exit: + *val = (err ? 0 : v); + return err; +} + +/** Combine a directory path with a relative path to produce + * a new path. + * + * @param s directory path + * @param t relative path + * @return new combined path s/t + */ +int path_concat(char *s, char *t, char **val){ + int err = 0; + int sn, tn, vn; + char *v; + sn = strlen(s); + if(sn > 0 && s[sn-1] == '/'){ + sn--; + } + tn = strlen(t); + if(tn > 0 && t[0] == '/'){ + tn--; + } + vn = sn+tn+1; + v = (char*)allocate(vn+1); + if(!v){ + err = -ENOMEM; + goto exit; + } + strncpy(v, s, sn); + v[sn] = '/'; + strncpy(v+sn+1, t, tn); + v[vn] = '\0'; + exit: + *val = (err ? NULL : v); + return err; +} diff --git a/tools/lib/sys_string.h b/tools/lib/sys_string.h new file mode 100644 index 0000000000..f39935f669 --- /dev/null +++ b/tools/lib/sys_string.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2001 - 2004 Mike Wray + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _XEN_LIB_SYS_STRING_H_ +#define _XEN_LIB_SYS_STRING_H_ +/** @file + * Replacement for standard string includes. + * Works in user or kernel code. + */ +/*============================================================================*/ +#ifdef __KERNEL__ + +#include +#include +#include +#include +#include +#include "allocate.h" + +#if 0 +static inline int tolower(int c){ + return (c>='A' && c<='Z' ? (c-'A')+'a' : c); +} +#endif + +static inline int isalpha(int c){ + return (c>='A' && c<='Z') || (c>='a' && c<='z'); +} + +static inline int isdigit(int c){ + return (c>='0' && c<='9'); +} + +#if 0 +static inline int strcasecmp(const char *s1, const char *s2){ + int c1, c2; + + do { + c1 = tolower(*s1++); + c2 = tolower(*s2++); + } while (c1 && c1 == c2); + return c1 - c2; +} +#endif + +static inline char * strdup(const char *s){ + int n = (s ? 1+strlen(s) : 0); + char *copy = (n ? allocate(n) : NULL); + if(copy){ + strcpy(copy, s); + } + return copy; +} + +/*============================================================================*/ +#else +#include +#include + +#ifndef _GNU_SOURCE +static inline size_t strnlen(const char *s, size_t n){ + int k = 0; + if(s){ + for(k=0; *s && k +/** @file + * XDR packer/unpacker for elements. + * + * string -> [T_STRING] [len:u32] + * atom -> [T_ATOM] [len:u32] + * uint -> [T_UINT] [value] + * cons -> [T_CONS] + * null -> [T_NULL] + * none -> [T_NONE] + * bool -> [T_BOOL] { 0:u8 | 1:u8 } + * + * types packed as u16. + * + * So (a b c) -> [T_CONS] a [T_CONS] b [T_CONS] c [T_NULL] + * () -> [T_NULL] + */ + +int pack_bool(IOStream *io, int x){ + int err=0; + err = IOStream_print(io, "%c", 0xff & x); + if(err > 0) err = 0; + return err; +} + +int unpack_bool(IOStream *io, int *x){ + int err = 0; + int c; + c = IOStream_getc(io); + *x = (c < 0 ? 0 : c); + err = IOStream_error(io); + if(c < 0 && !err) err = -EIO; + return err; +} + +int pack_ushort(IOStream *io, unsigned short x){ + int err=0; + err = IOStream_print(io, "%c%c", + 0xff & (x >> 8), + 0xff & (x )); + if(err > 0) err = 0; + return err; +} + +int unpack_ushort(IOStream *io, unsigned short *x){ + int err = 0; + int i, c = 0; + *x = 0; + for(i = 0; i< 2; i++){ + c = IOStream_getc(io); + if(c < 0) break; + *x <<= 8; + *x |= (0xff & c); + } + err = IOStream_error(io); + if(c < 0 && !err) err = -EIO; + return err; +} + +int pack_uint(IOStream *io, unsigned int x){ + int err=0; + err = IOStream_print(io, "%c%c%c%c", + 0xff & (x >> 24), + 0xff & (x >> 16), + 0xff & (x >> 8), + 0xff & (x )); + if(err > 0) err = 0; + return err; +} + +int unpack_uint(IOStream *io, unsigned int *x){ + int err = 0; + int i, c = 0; + *x = 0; + for(i = 0; i< 4; i++){ + c = IOStream_getc(io); + if(c < 0) break; + *x <<= 8; + *x |= (0xff & c); + } + err = IOStream_error(io); + if(c < 0 && !err) err = -EIO; + return err; +} + +int pack_string(IOStream *io, Sxpr x){ + int err = 0; + int n = string_length(x); + char *s = string_string(x); + int i; + err = pack_uint(io, n); + if(err) goto exit; + for(i = 0; i < n; i++){ + err = IOStream_print(io, "%c", s[i]); + if(err < 0) break; + } + if(err > 0) err = 0; + exit: + return err; +} + +int unpack_string(IOStream *io, Sxpr *x){ + int err; + unsigned int n; + int i, c = 0; + char *s; + Sxpr val = ONONE; + + err = unpack_uint(io, &n); + if(err) goto exit; + val = halloc(n+1, T_STRING); + if(NOMEMP(val)){ + err = -ENOMEM; + goto exit; + } + s = string_string(val); + for(i=0; iname); + break; + case T_STRING: + err = pack_string(io, x); + break; + case T_UINT: + err = pack_uint(io, get_ul(x)); + break; + default: + err = -EINVAL; + IOStream_print(iostderr, "%s> invalid type %d\n", __FUNCTION__, type); + break; + } + exit: + return err; +} + +int unpack_sxpr(IOStream *io, Sxpr *x){ + int err = 0; + unsigned short type; + unsigned int u; + Sxpr val = ONONE, y; + + err = unpack_ushort(io, &type); + if(err) goto exit; + switch(type){ + case T_NULL: + val = ONULL; + break; + case T_NONE: + val = ONONE; + break; + case T_CONS: + err = unpack_cons(io, &val); + break; + case T_BOOL: + err = unpack_bool(io, &u); + if(err) goto exit; + val = (u ? OTRUE : OFALSE); + break; + case T_ATOM: + err = unpack_string(io, &y); + if(err) goto exit; + val = intern(string_string(y)); + objfree(y); + break; + case T_STRING: + err = unpack_string(io, &val); + break; + case T_UINT: + err = unpack_uint(io, &u); + if(err) goto exit; + val = OBJI(type, u); + break; + default: + err = -EINVAL; + IOStream_print(iostderr, "%s> invalid type %d\n", __FUNCTION__, type); + break; + } + exit: + *x = (err ? ONONE : val); + return err; +} diff --git a/tools/lib/xdr.h b/tools/lib/xdr.h new file mode 100644 index 0000000000..cb7d97df95 --- /dev/null +++ b/tools/lib/xdr.h @@ -0,0 +1,14 @@ +/* $Id: xdr.h,v 1.2 2003/09/29 13:40:00 mjw Exp $ */ +#ifndef _SP_XDR_H_ +#define _SP_XDR_H_ +#include "iostream.h" +#include "sxpr.h" +int pack_uint(IOStream *out, unsigned int x); +int unpack_uint(IOStream *in, unsigned int *x); +int pack_string(IOStream *out, Sxpr x); +int unpack_string(IOStream *in, Sxpr *x); +int pack_cons(IOStream *out, Sxpr x); +int unpack_cons(IOStream *in, Sxpr *x); +int pack_sxpr(IOStream *out, Sxpr x); +int unpack_sxpr(IOStream *in, Sxpr *x); +#endif /* _SP_XDR_H_ */ diff --git a/tools/xc/lib/Makefile b/tools/xc/lib/Makefile index 271a9c4703..a94a082004 100644 --- a/tools/xc/lib/Makefile +++ b/tools/xc/lib/Makefile @@ -4,13 +4,71 @@ MINOR = 0 SONAME = libxc.so.$(MAJOR) CC = gcc -CFLAGS = -c -Werror -O3 -fno-strict-aliasing -CFLAGS += -I../../../xen/include/hypervisor-ifs -CFLAGS += -I../../xu/lib -CFLAGS += -I../../../linux-xen-sparse/include -HDRS = $(wildcard *.h) -OBJS = $(patsubst %.c,%.o,$(wildcard *.c)) +XEN_ROOT = ../../.. + +vpath %.h $(XEN_ROOT)/xen/include/hypervisor-ifs +INCLUDES += -I $(XEN_ROOT)/xen/include/hypervisor-ifs + +vpath %.h $(XEN_ROOT)/tools/xu/lib +INCLUDES += -I $(XEN_ROOT)/tools/xu/lib + +vpath %h $(XEN_ROOT)/linux-xen-sparse/include +INCLUDES += -I $(XEN_ROOT)/linux-xen-sparse/include + +vpath %c $(XEN_ROOT)/tools/lib +INCLUDES += -I $(XEN_ROOT)/tools/lib + +LIB_SRCS := +LIB_SRCS += allocate.c +#LIB_SRCS += enum.c +LIB_SRCS += file_stream.c +LIB_SRCS += gzip_stream.c +#LIB_SRCS += hash_table.c +LIB_SRCS += iostream.c +#LIB_SRCS += kernel_stream.c +#LIB_SRCS += lexis.c +#LIB_SRCS += lzi_stream.c +#LIB_SRCS += lzo_stream.c +#LIB_SRCS += marshal.c +#LIB_SRCS += socket_stream.c +#LIB_SRCS += string_stream.c +#LIB_SRCS += sxpr.c +#LIB_SRCS += sxpr_parser.c +LIB_SRCS += sys_net.c +LIB_SRCS += sys_string.c +#LIB_SRCS += xdr.c + +SRCS := +SRCS += xc_atropos.c +SRCS += xc_bvtsched.c +SRCS += xc_domain.c +SRCS += xc_evtchn.c +SRCS += xc_io.c +SRCS += xc_linux_build.c +SRCS += xc_linux_restore.c +SRCS += xc_linux_save.c +SRCS += xc_misc.c +SRCS += xc_netbsd_build.c +SRCS += xc_physdev.c +SRCS += xc_private.c +SRCS += $(LIB_SRCS) + +#CFLAGS += -I../../../xen/include/hypervisor-ifs +#CFLAGS += -I../../xu/lib +#CFLAGS += -I../../../linux-xen-sparse/include + +CFLAGS += -Wall +CFLAGS += -Werror +CFLAGS += -g +CFLAGS += -O3 +CFLAGS += -fno-strict-aliasing +CFLAGS += $(INCLUDES) +# Get gcc to generate the dependencies for us. +CFLAGS += -Wp,-MD,.$(@F).d +DEPS = .*.d + +OBJS = $(patsubst %.c,%.o,$(SRCS)) LIB = libxc.so libxc.so.$(MAJOR) libxc.so.$(MAJOR).$(MINOR) @@ -32,6 +90,8 @@ install: all clean: $(RM) *.a *.so *.o *.rpm $(LIB) + $(RM) *~ + $(RM) $(DEPS) rpm: all rm -rf staging @@ -49,5 +109,8 @@ libxc.so.$(MAJOR): libxc.so.$(MAJOR).$(MINOR): $(OBJS) $(CC) -Wl,-soname -Wl,$(SONAME) -shared -o $@ $^ -lz -%.o: %.c $(HDRS) Makefile - $(CC) $(CFLAGS) -o $@ $< +%.o: %.c Makefile + +# $(CC) $(CFLAGS) -o $@ $< + +-include $(DEPS) diff --git a/tools/xc/lib/xc.h b/tools/xc/lib/xc.h index 274f2fdded..adef112dbd 100644 --- a/tools/xc/lib/xc.h +++ b/tools/xc/lib/xc.h @@ -68,18 +68,10 @@ int xc_shadow_control(int xc_handle, #define XCFLAGS_LIVE 2 #define XCFLAGS_DEBUG 4 -int xc_linux_save(int xc_handle, - u32 domid, - unsigned int flags, - int (*writerfn)(void *, const void *, size_t), - void *writerst ); +struct XcIOContext; +int xc_linux_save(int xc_handle, struct XcIOContext *ioctxt); -int xc_linux_restore(int xc_handle, - u32 domid, - unsigned int flags, - int (*readerfn)(void *, void *, size_t), - void *readerst, - u32 *pdomid); +int xc_linux_restore(int xc_handle, struct XcIOContext *ioctxt); int xc_linux_build(int xc_handle, u32 domid, diff --git a/tools/xc/lib/xc_io.c b/tools/xc/lib/xc_io.c new file mode 100644 index 0000000000..7d75ea245d --- /dev/null +++ b/tools/xc/lib/xc_io.c @@ -0,0 +1,27 @@ +#include "xc_io.h" + +void xcio_error(XcIOContext *ctxt, const char *msg, ...){ + va_list args; + + va_start(args, msg); + IOStream_vprint(ctxt->info, msg, args); + va_end(args); +} + +void xcio_info(XcIOContext *ctxt, const char *msg, ...){ + va_list args; + + if(!(ctxt->flags & XCFLAGS_VERBOSE)) return; + va_start(args, msg); + IOStream_vprint(ctxt->info, msg, args); + va_end(args); +} + +void xcio_debug(XcIOContext *ctxt, const char *msg, ...){ + va_list args; + + if(!(ctxt->flags & XCFLAGS_DEBUG)) return; + va_start(args, msg); + IOStream_vprint(ctxt->info, msg, args); + va_end(args); +} diff --git a/tools/xc/lib/xc_io.h b/tools/xc/lib/xc_io.h new file mode 100644 index 0000000000..7a23cb91ae --- /dev/null +++ b/tools/xc/lib/xc_io.h @@ -0,0 +1,44 @@ +#ifndef __XC_XC_IO_H__ +#define __XC_XC_IO_H__ + +#include "xc_private.h" +#include + +typedef struct XcIOContext { + u32 domain; + unsigned flags; + IOStream *io; + IOStream *info; + IOStream *err; + char *vmconfig; + int vmconfig_n; +} XcIOContext; + +static inline int xcio_read(XcIOContext *ctxt, void *buf, int n){ + int rc; + + rc = IOStream_read(ctxt->io, buf, n); + return (rc == n ? 0 : rc); +} + +static inline int xcio_write(XcIOContext *ctxt, void *buf, int n){ + int rc; + + rc = IOStream_write(ctxt->io, buf, n); + return (rc == n ? 0 : rc); +} + +static inline int xcio_flush(XcIOContext *ctxt){ + return IOStream_flush(ctxt->io); +} + +extern void xcio_error(XcIOContext *ctxt, const char *msg, ...); +extern void xcio_info(XcIOContext *ctxt, const char *msg, ...); + +#define xcio_perror(_ctxt, _msg...) \ +xcio_error(_ctxt, "(errno %d %s)" _msg, errno, strerror(errno), ## _msg) + +#endif /* ! __XC_XC_IO_H__ */ + + + diff --git a/tools/xc/lib/xc_linux_restore.c b/tools/xc/lib/xc_linux_restore.c index 7387cddcf0..badba75162 100644 --- a/tools/xc/lib/xc_linux_restore.c +++ b/tools/xc/lib/xc_linux_restore.c @@ -8,7 +8,6 @@ #include "xc_private.h" #include -#include #define MAX_BATCH_SIZE 1024 @@ -21,14 +20,6 @@ #endif -/* This may allow us to create a 'quiet' command-line option, if necessary. */ -#define verbose_printf(_f, _a...) \ - do { \ - if ( !verbose ) break; \ - printf( _f , ## _a ); \ - fflush(stdout); \ - } while ( 0 ) - static int get_pfn_list(int xc_handle, u32 domain_id, unsigned long *pfn_buf, @@ -54,19 +45,44 @@ static int get_pfn_list(int xc_handle, return (ret < 0) ? -1 : op.u.getmemlist.num_pfns; } +/** Read the vmconfig string from the state input. + * It is stored as a 4-byte count 'n' followed by n bytes. + * The config data is stored in a new string in 'ioctxt->vmconfig', + * and is null-terminated. The count is stored in 'ioctxt->vmconfig_n'. + * + * @param ioctxt i/o context + * @return 0 on success, non-zero on error. + */ +static int read_vmconfig(XcIOContext *ioctxt){ + int err = -1; + if(xcio_read(ioctxt, &ioctxt->vmconfig_n, sizeof(ioctxt->vmconfig_n))){ + goto exit; + } + ioctxt->vmconfig = malloc(ioctxt->vmconfig_n + 1); + if(!ioctxt->vmconfig) goto exit; + if(xcio_read(ioctxt, ioctxt->vmconfig, ioctxt->vmconfig_n)){ + goto exit; + } + ioctxt->vmconfig[ioctxt->vmconfig_n] = '\0'; + err = 0; + exit: + if(err){ + if(ioctxt->vmconfig){ + free(ioctxt->vmconfig); + } + ioctxt->vmconfig = NULL; + ioctxt->vmconfig_n = 0; + } + return err; +} -int xc_linux_restore(int xc_handle, - u32 dom, - unsigned int flags, - int (*readerfn)(void *, void *, size_t), - void *readerst, - u32 *pdomid) +int xc_linux_restore(int xc_handle, XcIOContext *ioctxt) { dom0_op_t op; - int rc = 1, i, j, n, k; + int rc = 1, i, n, k; unsigned long mfn, pfn, xpfn; unsigned int prev_pc, this_pc; - int verbose = flags & XCFLAGS_VERBOSE; + u32 dom = ioctxt->domain; int verify = 0; /* Number of page frames in use by this Linux session. */ @@ -115,8 +131,7 @@ int xc_linux_restore(int xc_handle, /* used by debug verify code */ unsigned long buf[PAGE_SIZE/sizeof(unsigned long)]; - if ( mlock(&ctxt, sizeof(ctxt) ) ) - { + if ( mlock(&ctxt, sizeof(ctxt) ) ) { /* needed for when we do the build dom0 op, but might as well do early */ PERROR("Unable to mlock ctxt"); @@ -124,35 +139,36 @@ int xc_linux_restore(int xc_handle, } /* Start writing out the saved-domain record. */ - if ( (*readerfn)(readerst, signature, 16) || - (memcmp(signature, "LinuxGuestRecord", 16) != 0) ) - { - ERROR("Unrecognised state format -- no signature found"); + if ( xcio_read(ioctxt, signature, 16) || + (memcmp(signature, "LinuxGuestRecord", 16) != 0) ) { + xcio_error(ioctxt, "Unrecognised state format -- no signature found"); goto out; } - if ( (*readerfn)(readerst, name, sizeof(name)) || - (*readerfn)(readerst, &nr_pfns, sizeof(unsigned long)) || - (*readerfn)(readerst, pfn_to_mfn_frame_list, PAGE_SIZE) ) - { - ERROR("Error when reading from state file"); + if ( xcio_read(ioctxt, name, sizeof(name)) || + xcio_read(ioctxt, &nr_pfns, sizeof(unsigned long)) || + xcio_read(ioctxt, pfn_to_mfn_frame_list, PAGE_SIZE) ) { + xcio_error(ioctxt, "Error reading header"); goto out; } - for ( i = 0; i < MAX_DOMAIN_NAME; i++ ) - { + if(read_vmconfig(ioctxt)){ + xcio_error(ioctxt, "Error writing vmconfig"); + goto out; + } + + for ( i = 0; i < MAX_DOMAIN_NAME; i++ ) { if ( name[i] == '\0' ) break; if ( name[i] & 0x80 ) { - ERROR("Random characters in domain name"); + xcio_error(ioctxt, "Random characters in domain name"); goto out; } } name[MAX_DOMAIN_NAME-1] = '\0'; - if ( nr_pfns > 1024*1024 ) - { - ERROR("Invalid state file -- pfn count out of range"); + if ( nr_pfns > 1024*1024 ) { + xcio_error(ioctxt, "Invalid state file -- pfn count out of range"); goto out; } @@ -162,22 +178,19 @@ int xc_linux_restore(int xc_handle, region_mfn = calloc(1, 4 * MAX_BATCH_SIZE); if ( (pfn_to_mfn_table == NULL) || (pfn_type == NULL) || - (region_mfn == NULL) ) - { + (region_mfn == NULL) ) { errno = ENOMEM; goto out; } - if ( mlock(region_mfn, 4 * MAX_BATCH_SIZE ) ) - { - ERROR("Could not mlock region_mfn"); + if ( mlock(region_mfn, 4 * MAX_BATCH_SIZE ) ) { + xcio_error(ioctxt, "Could not mlock region_mfn"); goto out; } /* Set the domain's name to that from the restore file */ - if ( xc_domain_setname( xc_handle, dom, name ) ) - { - ERROR("Could not set domain name"); + if ( xc_domain_setname( xc_handle, dom, name ) ) { + xcio_error(ioctxt, "Could not set domain name"); goto out; } @@ -187,7 +200,7 @@ int xc_linux_restore(int xc_handle, if ( xc_domain_setinitialmem(xc_handle, dom, nr_pfns * (PAGE_SIZE / 1024)) ) { - ERROR("Could not set domain initial memory"); + xcio_error(ioctxt, "Could not set domain initial memory"); goto out; } @@ -195,9 +208,8 @@ int xc_linux_restore(int xc_handle, op.cmd = DOM0_GETDOMAININFO; op.u.getdomaininfo.domain = (domid_t)dom; op.u.getdomaininfo.ctxt = NULL; - if ( do_dom0_op(xc_handle, &op) < 0 ) - { - ERROR("Could not get information on new domain"); + if ( do_dom0_op(xc_handle, &op) < 0 ) { + xcio_error(ioctxt, "Could not get information on new domain"); goto out; } shared_info_frame = op.u.getdomaininfo.shared_info_frame; @@ -208,19 +220,17 @@ int xc_linux_restore(int xc_handle, /* Build the pfn-to-mfn table. We choose MFN ordering returned by Xen. */ - if ( get_pfn_list(xc_handle, dom, pfn_to_mfn_table, nr_pfns) != nr_pfns ) - { - ERROR("Did not read correct number of frame numbers for new dom"); + if ( get_pfn_list(xc_handle, dom, pfn_to_mfn_table, nr_pfns) != nr_pfns ) { + xcio_error(ioctxt, "Did not read correct number of frame numbers for new dom"); goto out; } - if ( (mmu = init_mmu_updates(xc_handle, dom)) == NULL ) - { - ERROR("Could not initialise for MMU updates"); + if ( (mmu = init_mmu_updates(xc_handle, dom)) == NULL ) { + xcio_error(ioctxt, "Could not initialise for MMU updates"); goto out; } - verbose_printf("Reloading memory pages: 0%%"); + xcio_info(ioctxt, "Reloading memory pages: 0%%"); /* * Now simply read each saved frame into its new machine frame. @@ -229,56 +239,45 @@ int xc_linux_restore(int xc_handle, prev_pc = 0; n=0; - while(1) - { + while(1) { int j; unsigned long region_pfn_type[MAX_BATCH_SIZE]; this_pc = (n * 100) / nr_pfns; - if ( (this_pc - prev_pc) >= 5 ) - { - verbose_printf("\b\b\b\b%3d%%", this_pc); + if ( (this_pc - prev_pc) >= 5 ) { + xcio_info(ioctxt, "\b\b\b\b%3d%%", this_pc); prev_pc = this_pc; } - if ( (*readerfn)(readerst, &j, sizeof(int)) ) - { - ERROR("Error when reading from state file"); + if ( xcio_read(ioctxt, &j, sizeof(int)) ) { + xcio_error(ioctxt, "Error when reading from state file"); goto out; } DPRINTF("batch %d\n",j); - if ( j == -1 ) - { + if ( j == -1 ) { verify = 1; printf("Entering page verify mode\n"); continue; } - if ( j == 0 ) - break; /* our work here is done */ + if ( j == 0 ) break; /* our work here is done */ - if( j > MAX_BATCH_SIZE ) - { - ERROR("Max batch size exceeded. Giving up."); + if( j > MAX_BATCH_SIZE ) { + xcio_error(ioctxt, "Max batch size exceeded. Giving up."); goto out; } - if ( (*readerfn)(readerst, region_pfn_type, j*sizeof(unsigned long)) ) - { - ERROR("Error when reading from state file"); + if ( xcio_read(ioctxt, region_pfn_type, j*sizeof(unsigned long)) ) { + xcio_error(ioctxt, "Error when reading from state file"); goto out; } - for(i=0;inr_pfns) - { - ERROR("pfn out of range"); + if (pfn>nr_pfns) { + xcio_error(ioctxt, "pfn out of range"); goto out; } @@ -314,36 +309,32 @@ int xc_linux_restore(int xc_handle, mfn = pfn_to_mfn_table[pfn]; - if ( verify ) + if ( verify ) { ppage = (unsigned long*) buf; /* debug case */ - else + } else { ppage = (unsigned long*) (region_base + i*PAGE_SIZE); + } - if ( (*readerfn)(readerst, ppage, PAGE_SIZE) ) - { - ERROR("Error when reading from state file"); + if ( xcio_read(ioctxt, ppage, PAGE_SIZE) ) { + xcio_error(ioctxt, "Error when reading from state file"); goto out; } - switch( region_pfn_type[i] ) - { + switch( region_pfn_type[i] ) { case 0: break; case L1TAB: { - for ( k = 0; k < 1024; k++ ) - { - if ( ppage[k] & _PAGE_PRESENT ) - { + for ( k = 0; k < 1024; k++ ) { + if ( ppage[k] & _PAGE_PRESENT ) { xpfn = ppage[k] >> PAGE_SHIFT; - if ( xpfn >= nr_pfns ) - { - ERROR("Frame number in type %d page table is " - "out of range. i=%d k=%d pfn=0x%x " - "nr_pfns=%d", region_pfn_type[i]>>28, i, - k, xpfn,nr_pfns); + if ( xpfn >= nr_pfns ) { + xcio_error(ioctxt, "Frame number in type %lu page table is " + "out of range. i=%d k=%d pfn=0x%lx " + "nr_pfns=%lu", region_pfn_type[i]>>28, i, + k, xpfn, nr_pfns); goto out; } @@ -359,16 +350,13 @@ int xc_linux_restore(int xc_handle, { for ( k = 0; k < (HYPERVISOR_VIRT_START>>L2_PAGETABLE_SHIFT); - k++ ) - { - if ( ppage[k] & _PAGE_PRESENT ) - { + k++ ) { + if ( ppage[k] & _PAGE_PRESENT ) { xpfn = ppage[k] >> PAGE_SHIFT; - if ( xpfn >= nr_pfns ) - { - ERROR("Frame number in type %d page table is " - "out of range. i=%d k=%d pfn=%d nr_pfns=%d", + if ( xpfn >= nr_pfns ) { + xcio_error(ioctxt, "Frame number in type %lu page table is " + "out of range. i=%d k=%d pfn=%lu nr_pfns=%lu", region_pfn_type[i]>>28, i, k, xpfn, nr_pfns); goto out; @@ -383,24 +371,21 @@ int xc_linux_restore(int xc_handle, break; default: - ERROR("Bogus page type %x page table is out of range." - " i=%d nr_pfns=%d", region_pfn_type[i], i, nr_pfns); + xcio_error(ioctxt, "Bogus page type %lx page table is out of range." + " i=%d nr_pfns=%lu", region_pfn_type[i], i, nr_pfns); goto out; } /* end of page type switch statement */ - if ( verify ) - { + if ( verify ) { int res = memcmp(buf, (region_base + i*PAGE_SIZE), PAGE_SIZE ); - if (res) - { + if (res) { int v; - printf("************** pfn=%x type=%x gotcs=%08lx " + printf("************** pfn=%lx type=%lx gotcs=%08lx " "actualcs=%08lx\n", pfn, pfn_type[pfn], csum_page(region_base + i*PAGE_SIZE), csum_page(buf)); - for ( v = 0; v < 4; v++ ) - { + for ( v = 0; v < 4; v++ ) { unsigned long *p = (unsigned long *) (region_base + i*PAGE_SIZE); if ( buf[v] != p[v] ) @@ -411,8 +396,7 @@ int xc_linux_restore(int xc_handle, } if ( add_mmu_update(xc_handle, mmu, - (mfn<= nr_pfns) || (pfn_type[pfn] != NOTAB) ) { - ERROR("Suspend record frame number is bad"); + xcio_error(ioctxt, "Suspend record frame number is bad"); goto out; } ctxt.cpu_ctxt.esi = mfn = pfn_to_mfn_table[pfn]; @@ -487,17 +463,14 @@ int xc_linux_restore(int xc_handle, unmap_pfn(pm_handle, p_srec); /* Uncanonicalise each GDT frame number. */ - if ( ctxt.gdt_ents > 8192 ) - { - ERROR("GDT entry count out of range"); + if ( ctxt.gdt_ents > 8192 ) { + xcio_error(ioctxt, "GDT entry count out of range"); goto out; } - for ( i = 0; i < ctxt.gdt_ents; i += 512 ) - { + for ( i = 0; i < ctxt.gdt_ents; i += 512 ) { pfn = ctxt.gdt_frames[i]; - if ( (pfn >= nr_pfns) || (pfn_type[pfn] != NOTAB) ) - { - ERROR("GDT frame number is bad"); + if ( (pfn >= nr_pfns) || (pfn_type[pfn] != NOTAB) ) { + xcio_error(ioctxt, "GDT frame number is bad"); goto out; } ctxt.gdt_frames[i] = pfn_to_mfn_table[pfn]; @@ -505,11 +478,10 @@ int xc_linux_restore(int xc_handle, /* Uncanonicalise the page table base pointer. */ pfn = ctxt.pt_base >> PAGE_SHIFT; - if ( (pfn >= nr_pfns) || (pfn_type[pfn] != L2TAB) ) - { - printf("PT base is bad. pfn=%d nr=%d type=%08lx %08lx\n", - pfn, nr_pfns, pfn_type[pfn], L2TAB); - ERROR("PT base is bad."); + if ( (pfn >= nr_pfns) || (pfn_type[pfn] != L2TAB) ) { + printf("PT base is bad. pfn=%lu nr=%lu type=%08lx %08lx\n", + pfn, nr_pfns, pfn_type[pfn], (unsigned long)L2TAB); + xcio_error(ioctxt, "PT base is bad."); goto out; } ctxt.pt_base = pfn_to_mfn_table[pfn] << PAGE_SHIFT; @@ -527,14 +499,12 @@ int xc_linux_restore(int xc_handle, /* Uncanonicalise the pfn-to-mfn table frame-number list. */ - for ( i = 0; i < (nr_pfns+1023)/1024; i++ ) - { + for ( i = 0; i < (nr_pfns+1023)/1024; i++ ) { unsigned long pfn, mfn; pfn = pfn_to_mfn_frame_list[i]; - if ( (pfn >= nr_pfns) || (pfn_type[pfn] != NOTAB) ) - { - ERROR("PFN-to-MFN frame number is bad"); + if ( (pfn >= nr_pfns) || (pfn_type[pfn] != NOTAB) ) { + xcio_error(ioctxt, "PFN-to-MFN frame number is bad"); goto out; } mfn = pfn_to_mfn_table[pfn]; @@ -545,9 +515,8 @@ int xc_linux_restore(int xc_handle, mfn_mapper_map_batch(xc_handle, dom, PROT_WRITE, pfn_to_mfn_frame_list, - (nr_pfns+1023)/1024 )) == 0 ) - { - ERROR("Couldn't map pfn_to_mfn table"); + (nr_pfns+1023)/1024 )) == 0 ) { + xcio_error(ioctxt, "Couldn't map pfn_to_mfn table"); goto out; } @@ -569,24 +538,26 @@ int xc_linux_restore(int xc_handle, * 9. debugregs are checked by Xen. * 10. callback code selectors need checking. */ - for ( i = 0; i < 256; i++ ) - { + for ( i = 0; i < 256; i++ ) { ctxt.trap_ctxt[i].vector = i; if ( (ctxt.trap_ctxt[i].cs & 3) == 0 ) ctxt.trap_ctxt[i].cs = FLAT_GUESTOS_CS; } - if ( (ctxt.guestos_ss & 3) == 0 ) + if ( (ctxt.guestos_ss & 3) == 0 ){ ctxt.guestos_ss = FLAT_GUESTOS_DS; - if ( (ctxt.event_callback_cs & 3) == 0 ) + } + if ( (ctxt.event_callback_cs & 3) == 0 ){ ctxt.event_callback_cs = FLAT_GUESTOS_CS; - if ( (ctxt.failsafe_callback_cs & 3) == 0 ) + } + if ( (ctxt.failsafe_callback_cs & 3) == 0 ){ ctxt.failsafe_callback_cs = FLAT_GUESTOS_CS; + } if ( ((ctxt.ldt_base & (PAGE_SIZE - 1)) != 0) || (ctxt.ldt_ents > 8192) || (ctxt.ldt_base > HYPERVISOR_VIRT_START) || ((ctxt.ldt_base + ctxt.ldt_ents*8) > HYPERVISOR_VIRT_START) ) { - ERROR("Bad LDT base or size"); + xcio_error(ioctxt, "Bad LDT base or size"); goto out; } @@ -597,34 +568,33 @@ int xc_linux_restore(int xc_handle, /* don't start the domain as we have console etc to set up */ - if( rc == 0 ) - { + if( rc == 0 ) { /* Success: print the domain id. */ - verbose_printf("DOM=%u\n", dom); + xcio_info(ioctxt, "DOM=%lu\n", dom); return 0; } out: - if ( (rc != 0) && (dom != 0) ) + if ( (rc != 0) && (dom != 0) ){ xc_domain_destroy(xc_handle, dom); - - if ( mmu != NULL ) + } + if ( mmu != NULL ){ free(mmu); - - if ( pm_handle >= 0 ) + } + if ( pm_handle >= 0 ){ (void)close_pfn_mapper(pm_handle); - - if ( pfn_to_mfn_table != NULL ) + } + if ( pfn_to_mfn_table != NULL ){ free(pfn_to_mfn_table); - if ( pfn_type != NULL ) + } + if ( pfn_type != NULL ){ free(pfn_type); + } - - if ( rc == 0 ) - *pdomid = dom; - + if ( rc == 0 ){ + ioctxt->domain = dom; + } DPRINTF("Restore exit with rc=%d\n",rc); - return rc; } diff --git a/tools/xc/lib/xc_linux_save.c b/tools/xc/lib/xc_linux_save.c index 2d035ff7ea..6eea1b4276 100644 --- a/tools/xc/lib/xc_linux_save.c +++ b/tools/xc/lib/xc_linux_save.c @@ -6,6 +6,7 @@ * Copyright (c) 2003, K A Fraser. */ +#include #include "xc_private.h" #include @@ -26,16 +27,6 @@ #define DDPRINTF(_f, _a...) ((void)0) #endif - - -/* This may allow us to create a 'quiet' command-line option, if necessary. */ -#define verbose_printf(_f, _a...) \ - do { \ - if ( !verbose ) break; \ - printf( _f , ## _a ); \ - fflush(stdout); \ - } while ( 0 ) - /* * Returns TRUE if the given machine frame number has a unique mapping * in the guest's pseudophysical map. @@ -197,20 +188,30 @@ static int track_cpu_usage( int xc_handle, u32 domid, int faults, return 0; } +/** Write the vmconfig string. + * It is stored as a 4-byte count 'n' followed by n bytes. + * + * @param ioctxt i/o context + * @return 0 on success, non-zero on error. + */ +static int write_vmconfig(XcIOContext *ioctxt){ + int err = -1; + if(xcio_write(ioctxt, &ioctxt->vmconfig_n, sizeof(ioctxt->vmconfig_n))) goto exit; + if(xcio_write(ioctxt, ioctxt->vmconfig, ioctxt->vmconfig_n)) goto exit; + err = 0; + exit: + return err; +} -int xc_linux_save(int xc_handle, - u32 domid, - unsigned int flags, - int (*writerfn)(void *, const void *, size_t), - void *writerst ) +int xc_linux_save(int xc_handle, XcIOContext *ioctxt) { dom0_op_t op; int rc = 1, i, j, k, last_iter, iter = 0; unsigned long mfn; - int verbose = flags & XCFLAGS_VERBOSE; - int live = flags & XCFLAGS_LIVE; - int debug = flags & XCFLAGS_DEBUG; - int sent_last_iter, sent_this_iter, skip_this_iter; + u32 domid = ioctxt->domain; + int live = (ioctxt->flags & XCFLAGS_LIVE); + int debug = (ioctxt->flags & XCFLAGS_DEBUG); + int sent_last_iter, skip_this_iter; unsigned long dirtied_this_iter, faults_this_iter; /* Important tuning parameters */ @@ -246,7 +247,7 @@ int xc_linux_save(int xc_handle, unsigned long *live_shinfo; /* base of the region in which domain memory is mapped */ - unsigned char *region_base; + unsigned char *region_base = NULL; /* A temporary mapping, and a copy, of the guest's suspend record. */ suspend_record_t *p_srec; @@ -266,16 +267,14 @@ int xc_linux_save(int xc_handle, int needed_to_fix = 0; int total_sent = 0; - if ( mlock(&ctxt, sizeof(ctxt) ) ) - { - PERROR("Unable to mlock ctxt"); + if (mlock(&ctxt, sizeof(ctxt))) { + xcio_perror(ioctxt, "Unable to mlock ctxt"); return 1; } /* Ensure that the domain exists, and that it is stopped. */ - if ( xc_domain_pause(xc_handle, domid) ) - { - PERROR("Could not pause domain"); + if ( xc_domain_pause(xc_handle, domid) ){ + xcio_perror(ioctxt, "Could not pause domain"); goto out; } @@ -283,9 +282,8 @@ int xc_linux_save(int xc_handle, shared_info_frame = op.u.getdomaininfo.shared_info_frame; /* A cheesy test to see whether the domain contains valid state. */ - if ( ctxt.pt_base == 0 ) - { - ERROR("Domain is not in a valid Linux guest OS state"); + if ( ctxt.pt_base == 0 ){ + xcio_error(ioctxt, "Domain is not in a valid Linux guest OS state"); goto out; } @@ -293,20 +291,17 @@ int xc_linux_save(int xc_handle, domid for this to succeed. */ p_srec = mfn_mapper_map_single(xc_handle, domid, sizeof(*p_srec), PROT_READ, - ctxt.cpu_ctxt.esi ); - - if (!p_srec) - { - ERROR("Couldn't map state record"); + ctxt.cpu_ctxt.esi); + if (!p_srec){ + xcio_error(ioctxt, "Couldn't map state record"); goto out; } nr_pfns = p_srec->nr_pfns; /* cheesy sanity check */ - if ( nr_pfns > 1024*1024 ) - { - ERROR("Invalid state record -- pfn count out of range"); + if ( nr_pfns > 1024*1024 ){ + xcio_error(ioctxt, "Invalid state record -- pfn count out of range"); goto out; } @@ -316,9 +311,8 @@ int xc_linux_save(int xc_handle, PAGE_SIZE, PROT_READ, p_srec->pfn_to_mfn_frame_list ); - if (!live_pfn_to_mfn_frame_list) - { - ERROR("Couldn't map pfn_to_mfn_frame_list"); + if (!live_pfn_to_mfn_frame_list){ + xcio_error(ioctxt, "Couldn't map pfn_to_mfn_frame_list"); goto out; } @@ -345,24 +339,21 @@ int xc_linux_save(int xc_handle, (its not clear why it would want to change them, and we'll be OK from a safety POV anyhow. */ - live_pfn_to_mfn_table = mfn_mapper_map_batch( xc_handle, domid, - PROT_READ, - live_pfn_to_mfn_frame_list, - (nr_pfns+1023)/1024 ); - if( !live_pfn_to_mfn_table ) - { - PERROR("Couldn't map pfn_to_mfn table"); + live_pfn_to_mfn_table = mfn_mapper_map_batch(xc_handle, domid, + PROT_READ, + live_pfn_to_mfn_frame_list, + (nr_pfns+1023)/1024 ); + if( !live_pfn_to_mfn_table ){ + xcio_perror(ioctxt, "Couldn't map pfn_to_mfn table"); goto out; } /* Canonicalise the pfn-to-mfn table frame-number list. */ memcpy( pfn_to_mfn_frame_list, live_pfn_to_mfn_frame_list, PAGE_SIZE ); - for ( i = 0; i < nr_pfns; i += 1024 ) - { - if ( !translate_mfn_to_pfn(&pfn_to_mfn_frame_list[i/1024]) ) - { - ERROR("Frame # in pfn-to-mfn frame list is not in pseudophys"); + for ( i = 0; i < nr_pfns; i += 1024 ){ + if ( !translate_mfn_to_pfn(&pfn_to_mfn_frame_list[i/1024]) ){ + xcio_error(ioctxt, "Frame # in pfn-to-mfn frame list is not in pseudophys"); goto out; } } @@ -370,27 +361,24 @@ int xc_linux_save(int xc_handle, /* At this point, we can start the domain again if we're doing a live suspend */ - if( live ) - { + if( live ){ if ( xc_shadow_control( xc_handle, domid, DOM0_SHADOW_CONTROL_OP_ENABLE_LOGDIRTY, - NULL, 0, NULL, NULL ) < 0 ) - { - ERROR("Couldn't enable shadow mode"); + NULL, 0, NULL, NULL ) < 0 ){ + xcio_error(ioctxt, "Couldn't enable shadow mode"); goto out; } - if ( xc_domain_unpause(xc_handle, domid) < 0 ) - { - ERROR("Couldn't unpause domain"); + if ( xc_domain_unpause(xc_handle, domid) < 0 ){ + xcio_error(ioctxt, "Couldn't unpause domain"); goto out; } last_iter = 0; sent_last_iter = 1<<20; /* 4GB's worth of pages */ - } - else + } else{ last_iter = 1; + } /* Setup to_send bitmap */ @@ -401,25 +389,22 @@ int xc_linux_save(int xc_handle, to_fix = calloc( 1, sz ); to_skip = malloc( sz ); - if (!to_send || !to_fix || !to_skip) - { - ERROR("Couldn't allocate to_send array"); + if (!to_send || !to_fix || !to_skip){ + xcio_error(ioctxt, "Couldn't allocate to_send array"); goto out; } memset( to_send, 0xff, sz ); - if ( mlock( to_send, sz ) ) - { - PERROR("Unable to mlock to_send"); + if ( mlock( to_send, sz ) ){ + xcio_perror(ioctxt, "Unable to mlock to_send"); return 1; } /* (to fix is local only) */ - if ( mlock( to_skip, sz ) ) - { - PERROR("Unable to mlock to_skip"); + if ( mlock( to_skip, sz ) ){ + xcio_perror(ioctxt, "Unable to mlock to_skip"); return 1; } @@ -429,21 +414,19 @@ int xc_linux_save(int xc_handle, 15->4 16->4 17->5 */ for( i=nr_pfns-1, order_nr=0; i ; i>>=1, order_nr++ ); - printf("nr_pfns=%d order_nr=%d\n",nr_pfns, order_nr); + printf("nr_pfns=%lu order_nr=%d\n",nr_pfns, order_nr); /* We want zeroed memory so use calloc rather than malloc. */ pfn_type = calloc(BATCH_SIZE, sizeof(unsigned long)); pfn_batch = calloc(BATCH_SIZE, sizeof(unsigned long)); - if ( (pfn_type == NULL) || (pfn_batch == NULL) ) - { + if ( (pfn_type == NULL) || (pfn_batch == NULL) ){ errno = ENOMEM; goto out; } - if ( mlock( pfn_type, BATCH_SIZE * sizeof(unsigned long) ) ) - { - ERROR("Unable to mlock"); + if ( mlock( pfn_type, BATCH_SIZE * sizeof(unsigned long) ) ){ + xcio_error(ioctxt, "Unable to mlock"); goto out; } @@ -452,8 +435,7 @@ int xc_linux_save(int xc_handle, * Quick belt and braces sanity check. */ #if DEBUG - for ( i = 0; i < nr_pfns; i++ ) - { + for ( i = 0; i < nr_pfns; i++ ){ mfn = live_pfn_to_mfn_table[i]; if( (live_mfn_to_pfn_table[mfn] != i) && (mfn != 0x80000004) ) @@ -467,29 +449,30 @@ int xc_linux_save(int xc_handle, PAGE_SIZE, PROT_READ, shared_info_frame); - if (!live_shinfo) - { - ERROR("Couldn't map live_shinfo"); + if (!live_shinfo){ + xcio_error(ioctxt, "Couldn't map live_shinfo"); goto out; } /* Start writing out the saved-domain record. */ - if ( (*writerfn)(writerst, "LinuxGuestRecord", 16) || - (*writerfn)(writerst, name, sizeof(name)) || - (*writerfn)(writerst, &nr_pfns, sizeof(unsigned long)) || - (*writerfn)(writerst, pfn_to_mfn_frame_list, PAGE_SIZE) ) - { - ERROR("Error when writing to state file (1)"); + if ( xcio_write(ioctxt, "LinuxGuestRecord", 16) || + xcio_write(ioctxt, name, sizeof(name)) || + xcio_write(ioctxt, &nr_pfns, sizeof(unsigned long)) || + xcio_write(ioctxt, pfn_to_mfn_frame_list, PAGE_SIZE) ){ + xcio_error(ioctxt, "Error writing header"); + goto out; + } + if(write_vmconfig(ioctxt)){ + xcio_error(ioctxt, "Error writing vmconfig"); goto out; } - track_cpu_usage( xc_handle, domid, 0, 0, 0, 0 ); + track_cpu_usage(xc_handle, domid, 0, 0, 0, 0 ); /* Now write out each data page, canonicalising page tables as we go... */ - while(1) - { + while(1){ unsigned int prev_pc, sent_this_iter, N, batch; iter++; @@ -498,15 +481,13 @@ int xc_linux_save(int xc_handle, prev_pc = 0; N=0; - verbose_printf("Saving memory pages: iter %d 0%%", iter); + xcio_info(ioctxt, "Saving memory pages: iter %d 0%%", iter); - while( N < nr_pfns ) - { + while( N < nr_pfns ){ unsigned int this_pc = (N * 100) / nr_pfns; - if ( (this_pc - prev_pc) >= 5 ) - { - verbose_printf("\b\b\b\b%3d%%", this_pc); + if ( (this_pc - prev_pc) >= 5 ){ + xcio_info(ioctxt, "\b\b\b\b%3d%%", this_pc); prev_pc = this_pc; } @@ -516,9 +497,8 @@ int xc_linux_save(int xc_handle, if ( !last_iter && xc_shadow_control(xc_handle, domid, DOM0_SHADOW_CONTROL_OP_PEEK, - to_skip, nr_pfns, NULL, NULL) != nr_pfns ) - { - ERROR("Error peeking shadow bitmap"); + to_skip, nr_pfns, NULL, NULL) != nr_pfns ){ + xcio_error(ioctxt, "Error peeking shadow bitmap"); goto out; } @@ -526,25 +506,26 @@ int xc_linux_save(int xc_handle, /* load pfn_type[] with the mfn of all the pages we're doing in this batch. */ - for( batch = 0; batch < BATCH_SIZE && N < nr_pfns ; N++ ) - { + for( batch = 0; batch < BATCH_SIZE && N < nr_pfns ; N++ ){ int n = permute(N, nr_pfns, order_nr ); if(0 && debug) fprintf(stderr,"%d pfn= %08lx mfn= %08lx %d " "[mfn]= %08lx\n", - iter, n, live_pfn_to_mfn_table[n], + iter, (unsigned long)n, live_pfn_to_mfn_table[n], test_bit(n,to_send), live_mfn_to_pfn_table[ live_pfn_to_mfn_table[n]&0xFFFFF]); - if (!last_iter && test_bit(n, to_send) && test_bit(n, to_skip)) + if (!last_iter && test_bit(n, to_send) && test_bit(n, to_skip)){ skip_this_iter++; /* stats keeping */ + } if (! ( (test_bit(n, to_send) && !test_bit(n, to_skip)) || (test_bit(n, to_send) && last_iter) || - (test_bit(n, to_fix) && last_iter) ) ) + (test_bit(n, to_fix) && last_iter) ) ){ continue; + } /* we get here if: 1. page is marked to_send & hasn't already been re-dirtied @@ -555,8 +536,7 @@ int xc_linux_save(int xc_handle, pfn_batch[batch] = n; pfn_type[batch] = live_pfn_to_mfn_table[n]; - if( pfn_type[batch] == 0x80000004 ) - { + if( pfn_type[batch] == 0x80000004 ){ /* not currently in pusedo-physical map -- set bit in to_fix that we must send this page in last_iter unless its sent sooner anyhow */ @@ -570,8 +550,7 @@ int xc_linux_save(int xc_handle, } if ( last_iter && test_bit(n, to_fix) && - !test_bit(n, to_send) ) - { + !test_bit(n, to_send) ){ needed_to_fix++; DPRINTF("Fix! iter %d, pfn %lx. mfn %lx\n", iter,n,pfn_type[batch]); @@ -584,33 +563,30 @@ int xc_linux_save(int xc_handle, DDPRINTF("batch %d:%d (n=%d)\n",iter,batch,n); - if ( batch == 0 ) + if ( batch == 0 ){ goto skip; /* very unlikely */ + } if ( (region_base = mfn_mapper_map_batch(xc_handle, domid, PROT_READ, pfn_type, - batch)) == 0 ) - { - PERROR("map batch failed"); + batch)) == 0 ){ + xcio_perror(ioctxt, "map batch failed"); goto out; } - if ( get_pfn_type_batch(xc_handle, domid, batch, pfn_type) ) - { - ERROR("get_pfn_type_batch failed"); + if ( get_pfn_type_batch(xc_handle, domid, batch, pfn_type) ){ + xcio_error(ioctxt, "get_pfn_type_batch failed"); goto out; } - for ( j = 0; j < batch; j++ ) - { - if ( (pfn_type[j] & LTAB_MASK) == XTAB ) - { + for ( j = 0; j < batch; j++ ){ + if ( (pfn_type[j] & LTAB_MASK) == XTAB ){ DDPRINTF("type fail: page %i mfn %08lx\n",j,pfn_type[j]); continue; } - if ( 0 && debug ) + if ( 0 && debug ){ fprintf(stderr,"%d pfn= %08lx mfn= %08lx " "[mfn]= %08lx sum= %08lx\n", iter, @@ -619,6 +595,7 @@ int xc_linux_save(int xc_handle, live_mfn_to_pfn_table[pfn_type[j]&(~LTAB_MASK)], csum_page(region_base + (PAGE_SIZE*j)) ); + } /* canonicalise mfn->pfn */ pfn_type[j] = (pfn_type[j] & LTAB_MASK) | @@ -626,32 +603,27 @@ int xc_linux_save(int xc_handle, } - if ( (*writerfn)(writerst, &batch, sizeof(int) ) ) - { - ERROR("Error when writing to state file (2)"); + if ( xcio_write(ioctxt, &batch, sizeof(int) ) ){ + xcio_error(ioctxt, "Error when writing to state file (2)"); goto out; } - if ( (*writerfn)(writerst, pfn_type, sizeof(unsigned long)*j ) ) - { - ERROR("Error when writing to state file (3)"); + if ( xcio_write(ioctxt, pfn_type, sizeof(unsigned long)*j ) ){ + xcio_error(ioctxt, "Error when writing to state file (3)"); goto out; } /* entering this loop, pfn_type is now in pfns (Not mfns) */ - for( j = 0; j < batch; j++ ) - { + for( j = 0; j < batch; j++ ){ /* write out pages in batch */ - if( (pfn_type[j] & LTAB_MASK) == XTAB) - { + if( (pfn_type[j] & LTAB_MASK) == XTAB){ DDPRINTF("SKIP BOGUS page %i mfn %08lx\n",j,pfn_type[j]); continue; } if ( ((pfn_type[j] & LTAB_MASK) == L1TAB) || - ((pfn_type[j] & LTAB_MASK) == L2TAB) ) - { + ((pfn_type[j] & LTAB_MASK) == L2TAB) ){ memcpy(page, region_base + (PAGE_SIZE*j), PAGE_SIZE); @@ -659,8 +631,7 @@ int xc_linux_save(int xc_handle, k < (((pfn_type[j] & LTAB_MASK) == L2TAB) ? (HYPERVISOR_VIRT_START >> L2_PAGETABLE_SHIFT) : 1024); - k++ ) - { + k++ ){ unsigned long pfn; if ( !(page[k] & _PAGE_PRESENT) ) continue; @@ -683,20 +654,16 @@ int xc_linux_save(int xc_handle, page[k] |= pfn << PAGE_SHIFT; } /* end of page table rewrite for loop */ - if ( (*writerfn)(writerst, page, PAGE_SIZE) ) - { - ERROR("Error when writing to state file (4)"); + if ( xcio_write(ioctxt, page, PAGE_SIZE) ){ + xcio_error(ioctxt, "Error when writing to state file (4)"); goto out; } - } /* end of it's a PT page */ - else - { /* normal page */ - - if ( (*writerfn)(writerst, region_base + (PAGE_SIZE*j), - PAGE_SIZE) ) - { - ERROR("Error when writing to state file (5)"); + } /* end of it's a PT page */ else { /* normal page */ + + if ( xcio_write(ioctxt, region_base + (PAGE_SIZE*j), + PAGE_SIZE) ){ + xcio_error(ioctxt, "Error when writing to state file (5)"); goto out; } } @@ -712,40 +679,35 @@ int xc_linux_save(int xc_handle, total_sent += sent_this_iter; - verbose_printf("\r %d: sent %d, skipped %d, ", + xcio_info(ioctxt, "\r %d: sent %d, skipped %d, ", iter, sent_this_iter, skip_this_iter ); - if ( last_iter ) - { + if ( last_iter ){ track_cpu_usage( xc_handle, domid, 0, sent_this_iter, 0, 1); - - verbose_printf("Total pages sent= %d (%.2fx)\n", + xcio_info(ioctxt, "Total pages sent= %d (%.2fx)\n", total_sent, ((float)total_sent)/nr_pfns ); - verbose_printf("(of which %d were fixups)\n", needed_to_fix ); + xcio_info(ioctxt, "(of which %d were fixups)\n", needed_to_fix ); } - if ( debug && last_iter ) - { + if (last_iter && debug){ int minusone = -1; memset( to_send, 0xff, nr_pfns/8 ); debug = 0; printf("Entering debug resend-all mode\n"); /* send "-1" to put receiver into debug mode */ - if ( (*writerfn)(writerst, &minusone, sizeof(int)) ) + if ( xcio_write(ioctxt, &minusone, sizeof(int)) ) { - ERROR("Error when writing to state file (6)"); + xcio_error(ioctxt, "Error when writing to state file (6)"); goto out; } continue; } - if ( last_iter ) - break; + if ( last_iter ) break; - if ( live ) - { + if ( live ) { if ( (iter >= max_iters) || (sent_this_iter+skip_this_iter < 50) || (total_sent > nr_pfns*max_factor) ) @@ -761,7 +723,7 @@ int xc_linux_save(int xc_handle, to_send, nr_pfns, &faults_this_iter, &dirtied_this_iter) != nr_pfns ) { - ERROR("Error flushing shadow PT"); + xcio_error(ioctxt, "Error flushing shadow PT"); goto out; } @@ -781,9 +743,9 @@ int xc_linux_save(int xc_handle, rc = 0; /* Zero terminate */ - if ( (*writerfn)(writerst, &rc, sizeof(int)) ) + if ( xcio_write(ioctxt, &rc, sizeof(int)) ) { - ERROR("Error when writing to state file (6)"); + xcio_error(ioctxt, "Error when writing to state file (6)"); goto out; } @@ -794,50 +756,42 @@ int xc_linux_save(int xc_handle, if ( (do_dom0_op(xc_handle, &op) < 0) || ((u32)op.u.getdomaininfo.domain != domid) ) { - PERROR("Could not get info on domain"); + xcio_perror(ioctxt, "Could not get info on domain"); goto out; } /* Canonicalise the suspend-record frame number. */ - if ( !translate_mfn_to_pfn(&ctxt.cpu_ctxt.esi) ) - { - ERROR("State record is not in range of pseudophys map"); + if ( !translate_mfn_to_pfn(&ctxt.cpu_ctxt.esi) ){ + xcio_error(ioctxt, "State record is not in range of pseudophys map"); goto out; } /* Canonicalise each GDT frame number. */ - for ( i = 0; i < ctxt.gdt_ents; i += 512 ) - { - if ( !translate_mfn_to_pfn(&ctxt.gdt_frames[i]) ) - { - ERROR("GDT frame is not in range of pseudophys map"); + for ( i = 0; i < ctxt.gdt_ents; i += 512 ) { + if ( !translate_mfn_to_pfn(&ctxt.gdt_frames[i]) ) { + xcio_error(ioctxt, "GDT frame is not in range of pseudophys map"); goto out; } } /* Canonicalise the page table base pointer. */ - if ( !MFN_IS_IN_PSEUDOPHYS_MAP(ctxt.pt_base >> PAGE_SHIFT) ) - { - ERROR("PT base is not in range of pseudophys map"); + if ( !MFN_IS_IN_PSEUDOPHYS_MAP(ctxt.pt_base >> PAGE_SHIFT) ) { + xcio_error(ioctxt, "PT base is not in range of pseudophys map"); goto out; } ctxt.pt_base = live_mfn_to_pfn_table[ctxt.pt_base >> PAGE_SHIFT] << PAGE_SHIFT; - if ( (*writerfn)(writerst, &ctxt, sizeof(ctxt)) || - (*writerfn)(writerst, live_shinfo, PAGE_SIZE) ) - { - ERROR("Error when writing to state file (1)"); + if ( xcio_write(ioctxt, &ctxt, sizeof(ctxt)) || + xcio_write(ioctxt, live_shinfo, PAGE_SIZE) ) { + xcio_error(ioctxt, "Error when writing to state file (1)"); goto out; } munmap(live_shinfo, PAGE_SIZE); out: - if ( pfn_type != NULL ) - free(pfn_type); - + if ( pfn_type != NULL ) free(pfn_type); DPRINTF("Save exit rc=%d\n",rc); - return !!rc; } diff --git a/tools/xc/lib/xc_private.h b/tools/xc/lib/xc_private.h index 81213cff21..2b70657e49 100644 --- a/tools/xc/lib/xc_private.h +++ b/tools/xc/lib/xc_private.h @@ -136,6 +136,8 @@ int close_pfn_mapper(int pm_handle); void *map_pfn_writeable(int pm_handle, unsigned long pfn); void *map_pfn_readonly(int pm_handle, unsigned long pfn); void unmap_pfn(int pm_handle, void *vaddr); +int get_pfn_type_batch(int xc_handle, u32 dom, int num, unsigned long *arr); +unsigned long csum_page (void * page); /* * MMU updates. @@ -202,4 +204,5 @@ void * mfn_mapper_queue_entry(mfn_mapper_t *t, int offset, long long xc_domain_get_cpu_usage( int xc_handle, domid_t domid ); +#include "xc_io.h" #endif /* __XC_PRIVATE_H__ */ diff --git a/tools/xc/py/Xc.c b/tools/xc/py/Xc.c index b52114aab2..f82cd0e98f 100644 --- a/tools/xc/py/Xc.c +++ b/tools/xc/py/Xc.c @@ -14,6 +14,8 @@ #include #include #include +#include "xc_private.h" +#include "gzip_stream.h" /* Needed for Python versions earlier than 2.3. */ #ifndef PyMODINIT_FUNC @@ -189,115 +191,31 @@ static PyObject *pyxc_domain_getinfo(PyObject *self, return list; } -static PyObject *tcp_save(XcObject *xc, u32 dom, char *url, unsigned flags){ -#define max_namelen 64 - char server[max_namelen]; - char *port_s; - int port=777; - int sd = -1; - struct hostent *h; - struct sockaddr_in s; - int sockbufsize; +static int file_save(XcObject *xc, XcIOContext *ctxt, char *state_file){ int rc = -1; - - int writerfn(void *fd, const void *buf, size_t count) { - int tot = 0, rc; - do { - rc = write( (int) fd, ((char*)buf)+tot, count-tot ); - if ( rc < 0 ) { perror("WRITE"); return rc; }; - tot += rc; - } - while ( tot < count ); - return 0; - } - - strncpy( server, url+strlen("tcp://"), max_namelen); - server[max_namelen-1]='\0'; - if ( (port_s = strchr(server,':')) != NULL ) { - *port_s = '\0'; - port = atoi(port_s+1); + int fd = -1; + int open_flags = (O_CREAT | O_EXCL | O_WRONLY); + int open_mode = 0644; + + fd = open(state_file, open_flags, open_mode); + if(fd < 0){ + xcio_perror(ctxt, "Could not open file for writing"); + goto exit; } - printf("X server=%s port=%d\n",server,port); - h = gethostbyname(server); - sd = socket(AF_INET, SOCK_STREAM,0); - if (sd < 0) goto serr; - s.sin_family = AF_INET; - bcopy ( h->h_addr, &(s.sin_addr.s_addr), h->h_length); - s.sin_port = htons(port); - if ( connect(sd, (struct sockaddr *) &s, sizeof(s)) ) goto serr; - sockbufsize=128*1024; - if ( setsockopt(sd, SOL_SOCKET, SO_SNDBUF, &sockbufsize, sizeof sockbufsize) < 0 ) goto serr; - - if ( xc_linux_save(xc->xc_handle, dom, flags, writerfn, (void*)sd) == 0 ) { - if ( read( sd, &rc, sizeof(int) ) != sizeof(int) ) goto serr; - - if ( rc == 0 ) { - printf("Migration succesful -- destroy local copy\n"); - xc_domain_destroy(xc->xc_handle, dom); - close(sd); - Py_INCREF(zero); - return zero; - } else { - errno = rc; - } + /* Compression rate 1: we want speed over compression. + * We're mainly going for those zero pages, after all. + */ + ctxt->io = gzip_stream_fdopen(fd, "wb1"); + if(!ctxt->io){ + xcio_perror(ctxt, "Could not allocate compression state"); + goto exit; } - - serr: - printf("Migration failed -- restart local copy\n"); - xc_domain_unpause(xc->xc_handle, dom); - PyErr_SetFromErrno(xc_error); - if ( sd >= 0 ) close(sd); - return NULL; - -} - -static PyObject *file_save(XcObject *xc, u32 dom, char *state_file, unsigned flags){ - int fd = -1; - gzFile gfd = NULL; - - int writerfn(void *fd, const void *buf, size_t count) { - int rc; - while ( ((rc = gzwrite( (gzFile*)fd, (void*)buf, count)) == -1) && - (errno = EINTR) ) - continue; - return ! (rc == count); - } - - if (strncmp(state_file,"file:",strlen("file:")) == 0){ - state_file += strlen("file:"); - } - - if ( (fd = open(state_file, O_CREAT|O_EXCL|O_WRONLY, 0644)) == -1 ) { - perror("Could not open file for writing"); - goto err; - } - /* - * Compression rate 1: we want speed over compression. - * We're mainly going for those zero pages, after all. - */ - if ( (gfd = gzdopen(fd, "wb1")) == NULL ) { - perror("Could not allocate compression state for state file"); - close(fd); - goto err; - } - if ( xc_linux_save(xc->xc_handle, dom, flags, writerfn, gfd) == 0 ) { - /* kill domain. We don't want to do this for checkpointing, but - if we don't do it here I think people will hurt themselves - by accident... */ - xc_domain_destroy(xc->xc_handle, dom); - gzclose(gfd); - close(fd); - - Py_INCREF(zero); - return zero; - } - - err: - PyErr_SetFromErrno(xc_error); - if ( gfd != NULL ) gzclose(gfd); - if ( fd >= 0 ) close(fd); - unlink(state_file); - return NULL; + rc = xc_linux_save(xc->xc_handle, ctxt); + exit: + if(ctxt->io) IOStream_close(ctxt->io); + if(fd >= 0) close(fd); + unlink(state_file); + return rc; } static PyObject *pyxc_linux_save(PyObject *self, @@ -306,33 +224,54 @@ static PyObject *pyxc_linux_save(PyObject *self, { XcObject *xc = (XcObject *)self; - u32 dom; + u32 dom; char *state_file; - int progress = 1, live = -1, debug = 0; + int progress = 1, debug = 0; unsigned int flags = 0; PyObject *val = NULL; + int rc = -1; + XcIOContext ioctxt = { .info = iostdout, .err = iostderr }; - static char *kwd_list[] = { "dom", "state_file", "progress", - "live", "debug", NULL }; + static char *kwd_list[] = { "dom", "state_file", "vmconfig", "progress", "debug", NULL }; - if ( !PyArg_ParseTupleAndKeywords(args, kwds, "is|iii", kwd_list, - &dom, &state_file, &progress, - &live, &debug) ) + if (!PyArg_ParseTupleAndKeywords(args, kwds, "is|siii", kwd_list, + &ioctxt.domain, + &state_file, + &ioctxt.vmconfig, + &progress, + &debug)){ + goto exit; + } + ioctxt.vmconfig_n = (ioctxt.vmconfig ? strlen(ioctxt.vmconfig) : 0); + if (progress) ioctxt.flags |= XCFLAGS_VERBOSE; + if (debug) ioctxt.flags |= XCFLAGS_DEBUG; + if(!state_file || state_file[0] == '\0') goto exit; + rc = file_save(xc, &ioctxt, state_file); + if(rc){ + PyErr_SetFromErrno(xc_error); goto exit; + } + //xc_domain_destroy(xc->xc_handle, dom); + Py_INCREF(zero); + val = zero; + exit: + return val; +} - if (progress) flags |= XCFLAGS_VERBOSE; - if (live == 1) flags |= XCFLAGS_LIVE; - if (debug) flags |= XCFLAGS_DEBUG; - if ( strncmp(state_file,"tcp:", strlen("tcp:")) == 0 ) { - /* default to live for tcp */ - if (live == -1) flags |= XCFLAGS_LIVE; - val = tcp_save(xc, dom, state_file, flags); - } else { - val = file_save(xc, dom, state_file, flags); +static int file_restore(XcObject *xc, XcIOContext *ioctxt, char *state_file){ + int rc = -1; + + ioctxt->io = gzip_stream_fopen(state_file, "rb"); + if (!ioctxt->io) { + xcio_perror(ioctxt, "Could not open file for reading"); + goto exit; } + + rc = xc_linux_restore(xc->xc_handle, ioctxt); exit: - return val; + if(ioctxt->io) IOStream_close(ioctxt->io); + return rc; } static PyObject *pyxc_linux_restore(PyObject *self, @@ -340,161 +279,37 @@ static PyObject *pyxc_linux_restore(PyObject *self, PyObject *kwds) { XcObject *xc = (XcObject *)self; + char *state_file; + int progress = 1, debug = 0; + u32 dom; + PyObject *val = NULL; + XcIOContext ioctxt = { .info = iostdout, .err = iostderr }; + int rc =-1; - char *state_file; - int progress = 1; - u32 dom; - unsigned int flags = 0; - - static char *kwd_list[] = { "dom", "state_file", "progress", NULL }; - - if ( !PyArg_ParseTupleAndKeywords(args, kwds, "is|i", kwd_list, - &dom, &state_file, &progress) ) - return NULL; - - if ( progress ) - flags |= XCFLAGS_VERBOSE; - - if ( strncmp(state_file,"tcp:", strlen("tcp:")) == 0 ) - { -#define max_namelen 64 - char server[max_namelen]; - char *port_s; - int port=777; - int ld = -1, sd = -1; - struct hostent *h; - struct sockaddr_in s, d, p; - socklen_t dlen, plen; - int sockbufsize; - int on = 1, rc = -1; - - int readerfn(void *fd, void *buf, size_t count) - { - int rc, tot = 0; - do { - rc = read( (int) fd, ((char*)buf)+tot, count-tot ); - if ( rc < 0 ) { perror("READ"); return rc; } - if ( rc == 0 ) { printf("read: need %d, tot=%d got zero\n", - count-tot, tot); return -1; } - tot += rc; - } - while ( tot < count ); - return 0; - } - - strncpy( server, state_file+strlen("tcp://"), max_namelen); - server[max_namelen-1]='\0'; - if ( (port_s = strchr(server,':')) != NULL ) - { - *port_s = '\0'; - port = atoi(port_s+1); - } - - printf("X server=%s port=%d\n",server,port); - - h = gethostbyname(server); - ld = socket (AF_INET,SOCK_STREAM,0); - if ( ld < 0 ) goto serr; - s.sin_family = AF_INET; - s.sin_addr.s_addr = htonl(INADDR_ANY); - s.sin_port = htons(port); - - if ( setsockopt(ld, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0 ) - goto serr; - - if ( bind(ld, (struct sockaddr *) &s, sizeof(s)) ) - goto serr; - - if ( listen(ld, 1) ) - goto serr; - - dlen=sizeof(struct sockaddr); - if ( (sd = accept(ld, (struct sockaddr *) &d, &dlen )) < 0 ) - goto serr; - - plen = sizeof(p); - if ( getpeername(sd, (struct sockaddr_in *) &p, - &plen) < 0 ) - goto serr; - - printf("Accepted connection from %s\n", inet_ntoa(p.sin_addr)); - - sockbufsize=128*1024; - if ( setsockopt(sd, SOL_SOCKET, SO_SNDBUF, &sockbufsize, - sizeof sockbufsize) < 0 ) - goto serr; - - rc = xc_linux_restore(xc->xc_handle, dom, flags, - readerfn, (void*)sd, &dom); - - write( sd, &rc, sizeof(int) ); + static char *kwd_list[] = { "state_file", "progress", "debug", NULL }; - if (rc == 0) - { - close(sd); - Py_INCREF(zero); - return zero; - } - errno = rc; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "is|ii", kwd_list, + &ioctxt.domain, + &state_file, + &progress, + &debug)){ + goto exit; + } + if (progress) ioctxt.flags |= XCFLAGS_VERBOSE; + if (debug) ioctxt.flags |= XCFLAGS_DEBUG; - serr: - PyErr_SetFromErrno(xc_error); - if ( ld >= 0 ) close(ld); - if ( sd >= 0 ) close(sd); - return NULL; - } - else - { - int fd = -1; - gzFile gfd = NULL; - - int readerfn(void *fd, void *buf, size_t count) - { - int rc; - while ( ((rc = gzread( (gzFile*)fd, (void*)buf, count)) == -1) && - (errno = EINTR) ) - continue; - return ! (rc == count); - } - - if ( strncmp(state_file,"file:",strlen("file:")) == 0 ) - state_file += strlen("file:"); - - if ( (fd = open(state_file, O_RDONLY)) == -1 ) - { - perror("Could not open file for writing"); - goto err; - } - - /* - * Compression rate 1: we want speed over compression. - * We're mainly going for those zero pages, after all. - */ - if ( (gfd = gzdopen(fd, "rb")) == NULL ) - { - perror("Could not allocate compression state for state file"); - close(fd); - goto err; - } - - - if ( xc_linux_restore(xc->xc_handle, dom, flags, - readerfn, gfd, &dom) == 0 ) - { - gzclose(gfd); - close(fd); - - Py_INCREF(zero); - return zero; - } - - err: + if(!state_file || state_file[0] == '\0') goto exit; + rc = file_restore(xc, &ioctxt, state_file); + if(rc){ PyErr_SetFromErrno(xc_error); - if ( gfd != NULL ) gzclose(gfd); - if ( fd >= 0 ) close(fd); - return NULL; + goto exit; } - + val = Py_BuildValue("{s:i,s:s}", + "dom", ioctxt.domain, + "vmconfig", ioctxt.vmconfig); + //? free(ioctxt.vmconfig); + exit: + return val; } static PyObject *pyxc_linux_build(PyObject *self, diff --git a/tools/xc/py/setup.py b/tools/xc/py/setup.py index b295ed295b..8efe5ca1a0 100644 --- a/tools/xc/py/setup.py +++ b/tools/xc/py/setup.py @@ -3,12 +3,17 @@ from distutils.core import setup, Extension module = Extension("xc", extra_compile_args = ["-fno-strict-aliasing"], - include_dirs = ["../lib"], - library_dirs = ["../lib"], + include_dirs = ["../lib", + "../../../xen/include/hypervisor-ifs", + "../../../linux-xen-sparse/include", + "../../xu/lib", + "../../lib" ], + library_dirs = ["../lib", + "../../lib" ], libraries = ["xc"], sources = ["Xc.c"]) setup(name = "xc", - version = "1.0", + version = "2.0", ext_package = "xen.ext", ext_modules = [module]) diff --git a/tools/xen/lib/xend/XendDomain.py b/tools/xen/lib/xend/XendDomain.py index 446b7f7fae..d1e79a2b7d 100644 --- a/tools/xen/lib/xend/XendDomain.py +++ b/tools/xen/lib/xend/XendDomain.py @@ -235,7 +235,7 @@ class XendDomain: def domain_get(self, id): id = str(id) self.refresh_domain(id) - return self.domain[id] + return self.domain.get(id) def domain_unpause(self, id): """(Re)start domain running. @@ -278,22 +278,26 @@ class XendDomain: """ # Need a cancel too? pass - + def domain_save(self, id, dst, progress=0): """Save domain state to file, destroy domain. """ dom = int(id) + dominfo = self.domain_get(id) + if not dominfo: + return -1 + vmconfig = sxp.to_string(dominfo.sxpr()) self.domain_pause(id) eserver.inject('xend.domain.save', id) - rc = xc.linux_save(dom=dom, state_file=dst, progress=progress) + rc = xc.linux_save(dom=dom, state_file=dst, vmconfig=vmconfig, progress=progress) if rc == 0: self.domain_destroy(id) return rc - def domain_restore(self, src, config, progress=0): + def domain_restore(self, src, progress=0): """Restore domain from file. """ - dominfo = XendDomainInfo.dom_restore(dom, config) + dominfo = XendDomainInfo.vm_restore(src, progress=progress) self._add_domain(dominfo.id, dominfo) return dominfo diff --git a/tools/xen/lib/xend/XendDomainInfo.py b/tools/xen/lib/xend/XendDomainInfo.py index 964285ec13..05c7ba26b2 100644 --- a/tools/xen/lib/xend/XendDomainInfo.py +++ b/tools/xen/lib/xend/XendDomainInfo.py @@ -272,22 +272,23 @@ def vm_recreate(config, info): d.callback(vm) return d -def vm_restore(src, config, progress=0): +def vm_restore(src, progress=0): """Restore a VM from a disk image. src saved state to restore - config configuration progress progress reporting flag returns deferred raises VmError for invalid configuration """ vm = XendDomainInfo() - vm.config = config - ostype = "linux" #todo set from config + ostype = "linux" #todo Set from somewhere (store in the src?). restorefn = getattr(xc, "%s_restore" % ostype) - dom = restorefn(state_file=src, progress=progress) + d = restorefn(state_file=src, progress=progress) + dom = int(d['dom']) if dom < 0: raise VMError('restore failed') + vmconfig = sxp.from_string(d['vmconfig']) + vm.config = sxp.child_value(vmconfig, 'config') deferred = vm.dom_configure(dom) def vifs_cb(val, vm): vif_up(vm.ipaddrs) @@ -855,9 +856,7 @@ def vm_field_vfr(vm, config, val, index): if not ip: raise VmError('vfr: missing ip address') ipaddrs.append(ip); - #Don't do this in new i/o model. - #print 'vm_field_vfr> add rule', 'dom=', vm.dom, 'vif=', vif, 'ip=', ip - #xenctl.ip.setup_vfr_rules_for_vif(vm.dom, vif, ip) + # todo: Configure the ipaddrs. vm.ipaddrs = ipaddrs def vnet_bridge(vnet, vmac, dom, idx): diff --git a/tools/xen/lib/xend/server/SrvDomain.py b/tools/xen/lib/xend/server/SrvDomain.py index c3684f242b..7b9d3563a8 100644 --- a/tools/xen/lib/xend/server/SrvDomain.py +++ b/tools/xen/lib/xend/server/SrvDomain.py @@ -44,13 +44,6 @@ class SrvDomain(SrvDir): val = fn(req.args, {'dom': self.dom.id}) return val - def op_restore(self, op, req): - fn = FormFn(self.xd.domain_restore, - [['dom', 'int'], - ['file', 'str']]) - val = fn(req.args, {'dom': self.dom.id}) - return val - def op_migrate(self, op, req): fn = FormFn(self.xd.domain_migrate, [['dom', 'int'], diff --git a/tools/xen/lib/xend/server/SrvDomainDir.py b/tools/xen/lib/xend/server/SrvDomainDir.py index cb5d8e38cd..af4bc7a15c 100644 --- a/tools/xen/lib/xend/server/SrvDomainDir.py +++ b/tools/xen/lib/xend/server/SrvDomainDir.py @@ -7,6 +7,7 @@ from twisted.web import error from xen.xend import sxp from xen.xend import XendDomain +from xen.xend.Args import FormFn from SrvDir import SrvDir from SrvDomain import SrvDomain @@ -88,6 +89,12 @@ class SrvDomainDir(SrvDir): out.close() return val + def op_restore(self, op, req): + fn = FormFn(self.xd.domain_restore, + [['file', 'str']]) + val = fn(req.args) + return val + def render_POST(self, req): return self.perform(req) @@ -129,3 +136,9 @@ class SrvDomainDir(SrvDir): req.write('') req.write('Config
') req.write('') + req.write('
' + % req.prePathURL()) + req.write('') + req.write('State
') + req.write('
') + diff --git a/tools/xen/lib/xend/sxp.py b/tools/xen/lib/xend/sxp.py index dd4fece6f0..a08a0b6c0f 100644 --- a/tools/xen/lib/xend/sxp.py +++ b/tools/xen/lib/xend/sxp.py @@ -16,6 +16,7 @@ import sys import types import errno import string +from StringIO import StringIO __all__ = [ "mime_type", @@ -521,6 +522,28 @@ def elements(sxpr, ctxt=None): yield v i += 1 +def to_string(sxpr): + """Convert an sxpr to a string. + + sxpr sxpr + returns string + """ + io = StringIO() + sxp.show(sxpr, io) + io.seek(0) + val = io.getvalue() + io.close() + return val + +def from_string(str): + """Create an sxpr by parsing a string. + + str string + returns sxpr + """ + io = StringIO(str) + return parse(io) + def parse(io): """Completely parse all input from 'io'. diff --git a/tools/xen/xend b/tools/xen/xend index 65cfe8d820..77dcb1e75a 100644 --- a/tools/xen/xend +++ b/tools/xen/xend @@ -13,8 +13,8 @@ xend stop - Unfortunately restarting it upsets the channel to dom0 and - domain management stops working - needs a reboot to fix. + The daemon should reconnect to device control interfaces + and recover its state when restarted. """ import os import sys -- 2.30.2